Skip to content

Commit 09e6108

Browse files
committed
Supports static IP assignment in VMware Windows OVA
1 parent 826a2d7 commit 09e6108

File tree

3 files changed

+147
-5
lines changed

3 files changed

+147
-5
lines changed

Diff for: images/capi/hack/windows-ova-unattend.py

+140-4
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,104 @@
1515
# limitations under the License.
1616

1717

18+
import io
1819
import xml.etree.ElementTree as ET
1920
import os
2021
import argparse
2122
import json
2223

24+
25+
ADDRESSES_TEMPLATE = """ <IpAddress wcm:action="add" wcm:keyValue="%(order)d">%(cidr)s</IpAddress>
26+
"""
27+
INTERFACE_COMPONENT_TEMPLATE = """
28+
<component name="Microsoft-Windows-TCPIP" processorArchitecture="amd64"
29+
publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"
30+
xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State"
31+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
32+
<Interfaces>
33+
<Interface wcm:action="add">
34+
<Ipv4Settings>
35+
<DhcpEnabled>false</DhcpEnabled>
36+
</Ipv4Settings>
37+
<Ipv6Settings>
38+
<DhcpEnabled>false</DhcpEnabled>
39+
</Ipv6Settings>
40+
<Identifier>%(iface_id)s</Identifier>
41+
<UnicastIpAddresses>
42+
%(addresses)s </UnicastIpAddresses>
43+
<Routes>%(routes)s
44+
</Routes>
45+
</Interface>
46+
</Interfaces>
47+
</component>
48+
"""
49+
ROUTE_TEMPLATE = """
50+
<Route wcm:action="add">
51+
<Identifier>%(id)s</Identifier>
52+
<Prefix>%(prefix)s</Prefix>
53+
<NextHopAddress>%(gateway)s</NextHopAddress>
54+
</Route>"""
55+
DNS_TEMPLATE = """
56+
<component name="Microsoft-Windows-DNS-Client" processorArchitecture="amd64"
57+
publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"
58+
xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State"
59+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
60+
<Interfaces>%(interfaces)s
61+
</Interfaces>
62+
</component>
63+
"""
64+
DNS_INTERFACE_TEMPLATE = """
65+
<Interface wcm:action="add">
66+
<Identifier>%(iface_id)s</Identifier>
67+
<DNSServerSearchOrder>%(dns_search_orders)s
68+
</DNSServerSearchOrder>
69+
</Interface>
70+
"""
71+
DNS_SEARCH_ORDER_TEMPLATE = """
72+
<IpAddress wcm:action="add" wcm:keyValue="%(key)s">%(server)s</IpAddress>"""
73+
INTERFACE_ID = "Ethernet0"
74+
75+
2376
def set_xmlstring(root, location, key, value):
2477
setting = root.find(location)
2578
setting.find(key).text = value
2679
return setting
2780

81+
82+
def ensure_interfaces(root, interfaces_content):
83+
setting = root.find("*[@pass='specialize']")
84+
old_element = setting.find(".//*[@name='Microsoft-Windows-TCPIP']")
85+
modified = False
86+
if old_element:
87+
setting.remove(old_element)
88+
modified = not interfaces_content
89+
90+
if interfaces_content:
91+
interface_component_tree = ET.parse(io.StringIO(interfaces_content))
92+
new_element = interface_component_tree.getroot()
93+
setting.append(new_element)
94+
modified = True
95+
96+
return setting, modified
97+
98+
99+
def ensure_dns_settings(root, dns_content):
100+
setting = root.find("*[@pass='specialize']")
101+
old_element = setting.find(".//*[@name='Microsoft-Windows-DNS-Client']")
102+
modified = False
103+
if old_element:
104+
setting.remove(old_element)
105+
modified = not dns_content
106+
107+
if dns_content:
108+
dns_component_tree = ET.parse(io.StringIO(dns_content))
109+
new_element = dns_component_tree.getroot()
110+
setting.append(new_element)
111+
modified = True
112+
113+
return setting, modified
114+
115+
28116
def main():
29117
parser = argparse.ArgumentParser(
30118
description="Updates select variables in autounattend.xml")
@@ -55,7 +143,7 @@ def main():
55143
with open(args.var_file, 'r') as f:
56144
data = json.load(f)
57145

58-
modified=0
146+
modified = False
59147
os.chdir(args.build_dir)
60148
unattend=ET.parse(args.unattend_file)
61149
ET.register_namespace('', "urn:schemas-microsoft-com:unattend")
@@ -65,18 +153,66 @@ def main():
65153
root = unattend.getroot()
66154

67155
if data.get("unattend_timezone"):
68-
modified=1
156+
modified = True
69157
setting = set_xmlstring(root, ".//*[@pass='oobeSystem']/*[@name='Microsoft-Windows-Shell-Setup']",'{urn:schemas-microsoft-com:unattend}TimeZone', data["unattend_timezone"])
70158
print("windows-ova-unattend: Setting Timezone to %s" % data["unattend_timezone"])
71159

72160
admin_password = data.get("admin_password")
73161
if admin_password:
74-
modified=1
162+
modified = True
75163
set_xmlstring(root, ".//*[@pass='oobeSystem']/*[@name='Microsoft-Windows-Shell-Setup']/{*}UserAccounts/{*}AdministratorPassword",'{urn:schemas-microsoft-com:unattend}Value', admin_password)
76164
set_xmlstring(root, ".//*[@pass='oobeSystem']/*[@name='Microsoft-Windows-Shell-Setup']/{*}AutoLogon/{*}Password",'{urn:schemas-microsoft-com:unattend}Value', admin_password)
77165
print("windows-ova-unattend: Setting Administrator Password")
78166

79-
if modified == 1:
167+
addr_elements = []
168+
ip_addr_cidr = data.get("ipv4_address_cidr")
169+
gateway4 = data.get("gateway4")
170+
if ip_addr_cidr:
171+
modified = True
172+
route_configs = []
173+
if gateway4:
174+
route_configs.append(("0.0.0.0/0", gateway4))
175+
176+
routes = []
177+
for i in range(len(route_configs)):
178+
route_config = route_configs[i]
179+
routes.append(ROUTE_TEMPLATE % {"id": i+1, "prefix": route_config[0], "gateway": route_config[1]})
180+
print("windows-ova-unattend: Setting Gateway to %s" % route_config[1])
181+
182+
addrs = [ip_addr_cidr]
183+
for i in range(len(addrs)):
184+
addr_elements.append(ADDRESSES_TEMPLATE % {"order": i+1,
185+
"cidr": addrs[i]})
186+
print("windows-ova-unattend: Setting IP Address to %s" % ip_addr_cidr)
187+
188+
interfaces_content = None
189+
if addr_elements:
190+
interfaces_content = INTERFACE_COMPONENT_TEMPLATE % {"iface_id": INTERFACE_ID,
191+
"addresses": ''.join(addr_elements),
192+
"routes": ''.join(routes)}
193+
194+
setting, xml_modified = ensure_interfaces(root, interfaces_content)
195+
if xml_modified:
196+
modified = True
197+
198+
dns_servers = data.get("dns_servers")
199+
dns_servers_content = ''
200+
if dns_servers:
201+
dns_servers = dns_servers.split()
202+
search_order_content = []
203+
for i in range(len(dns_servers)):
204+
search_order_content.append(DNS_SEARCH_ORDER_TEMPLATE % {"key": i+1,
205+
"server": dns_servers[i]})
206+
dns_interface_content = [DNS_INTERFACE_TEMPLATE % {"iface_id": INTERFACE_ID,
207+
"dns_search_orders": ''.join(search_order_content)}]
208+
dns_servers_content = DNS_TEMPLATE % {"interfaces": ''.join(dns_interface_content)}
209+
print("windows-ova-unattend: Setting DNS Addresses to %s" % dns_servers)
210+
211+
setting, xml_modified = ensure_dns_settings(root, dns_servers_content)
212+
if xml_modified:
213+
modified = True
214+
215+
if modified:
80216
print("windows-ova-unattend: Updating %s ..." % args.unattend_file)
81217
unattend.write(args.unattend_file)
82218
else:

Diff for: images/capi/packer/ova/packer-common.json.tmpl

+3
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,16 @@
77
"disk_controller_type": "pvscsi",
88
"disk_thin_provisioned": "true",
99
"disk_type_id": "0",
10+
"dns_servers": null,
1011
"firmware": "bios",
1112
"format": "",
13+
"gateway4": null,
1214
"guestinfo_datasource_ref": "v1.4.1",
1315
"guestinfo_datasource_script": "{{user `guestinfo_datasource_slug`}}/{{user `guestinfo_datasource_ref`}}/install.sh",
1416
"guestinfo_datasource_slug": "https://raw.githubusercontent.com/vmware/cloud-init-vmware-guestinfo",
1517
"headless": "true",
1618
"insecure_connection": "false",
19+
"ipv4_address_cidr": null,
1720
"memory": "8192",
1821
"network": "",
1922
"network_card": "vmxnet3",

Diff for: images/capi/packer/ova/packer-windows.json

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"builders": [
33
{
4-
"content": "{\n \"unattend_timezone\" : \"{{user `unattend_timezone`}}\"\n, \"admin_password\" : \"{{user `windows_admin_password`}}\"\n}",
4+
"content": "{\n \"unattend_timezone\" : \"{{user `unattend_timezone`}}\"\n, \"admin_password\" : \"{{user `windows_admin_password`}}\"\n, \"ipv4_address_cidr\" : \"{{user `ipv4_address_cidr`}}\"\n, \"gateway4\" : \"{{user `gateway4`}}\"\n, \"dns_servers\" : \"{{user `dns_servers`}}\"\n}",
55
"target": "./packer_cache/unattend.json",
66
"type": "file"
77
},
@@ -249,10 +249,13 @@
249249
"containerd_version": null,
250250
"disable_hypervisor": null,
251251
"disk_size": "81920",
252+
"dns_servers": null,
252253
"firmware": "bios",
254+
"gateway4": null,
253255
"http_port_max": "",
254256
"http_port_min": "",
255257
"ib_version": "{{env `IB_VERSION`}}",
258+
"ipv4_address_cidr": null,
256259
"kubernetes_base_url": "https://kubernetesreleases.blob.core.windows.net/kubernetes/{{user `kubernetes_semver`}}/binaries/node/windows/{{user `kubernetes_goarch`}}",
257260
"kubernetes_http_package_url": "",
258261
"kubernetes_typed_version": "kube-{{user `kubernetes_semver`}}",

0 commit comments

Comments
 (0)