Skip to content

Commit 5301e60

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

File tree

3 files changed

+167
-20
lines changed

3 files changed

+167
-20
lines changed

images/capi/hack/windows-ova-unattend.py

+160-19
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,102 @@
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+
ADDRESSES_TEMPLATE = """ <IpAddress wcm:action="add" wcm:keyValue="%(order)d">%(cidr)s</IpAddress>
25+
"""
26+
INTERFACE_COMPONENT_TEMPLATE = """
27+
<component name="Microsoft-Windows-TCPIP" processorArchitecture="amd64"
28+
publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"
29+
xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State"
30+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
31+
<Interfaces>
32+
<Interface wcm:action="add">
33+
<Ipv4Settings>
34+
<DhcpEnabled>false</DhcpEnabled>
35+
</Ipv4Settings>
36+
<Ipv6Settings>
37+
<DhcpEnabled>false</DhcpEnabled>
38+
</Ipv6Settings>
39+
<Identifier>%(iface_id)s</Identifier>
40+
<UnicastIpAddresses>
41+
%(addresses)s </UnicastIpAddresses>
42+
<Routes>%(routes)s
43+
</Routes>
44+
</Interface>
45+
</Interfaces>
46+
</component>
47+
"""
48+
ROUTE_TEMPLATE = """
49+
<Route wcm:action="add">
50+
<Identifier>%(id)s</Identifier>
51+
<Prefix>%(prefix)s</Prefix>
52+
<NextHopAddress>%(gateway)s</NextHopAddress>
53+
</Route>"""
54+
DNS_TEMPLATE = """
55+
<component name="Microsoft-Windows-DNS-Client" processorArchitecture="amd64"
56+
publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS"
57+
xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State"
58+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
59+
<Interfaces>%(interfaces)s
60+
</Interfaces>
61+
</component>
62+
"""
63+
DNS_INTERFACE_TEMPLATE = """
64+
<Interface wcm:action="add">
65+
<Identifier>%(iface_id)s</Identifier>
66+
<DNSServerSearchOrder>%(dns_search_orders)s
67+
</DNSServerSearchOrder>
68+
</Interface>
69+
"""
70+
DNS_SEARCH_ORDER_TEMPLATE = """
71+
<IpAddress wcm:action="add" wcm:keyValue="%(key)s">%(server)s</IpAddress>"""
72+
INTERFACE_ID = "Ethernet0"
73+
74+
2375
def set_xmlstring(root, location, key, value):
24-
setting = root.find(location)
25-
setting.find(key).text = value
26-
return setting
76+
setting = root.find(location)
77+
setting.find(key).text = value
78+
return setting
79+
80+
81+
def ensure_interfaces(root, interfaces_content):
82+
setting = root.find("*[@pass='specialize']")
83+
old_element = setting.find(".//*[@name='Microsoft-Windows-TCPIP']")
84+
modified = False
85+
if old_element:
86+
setting.remove(old_element)
87+
modified = not interfaces_content
88+
89+
if interfaces_content:
90+
interface_component_tree = ET.parse(io.StringIO(interfaces_content))
91+
new_element = interface_component_tree.getroot()
92+
setting.append(new_element)
93+
modified = True
94+
95+
return setting, modified
96+
97+
98+
def ensure_dns_settings(root, dns_content):
99+
setting = root.find("*[@pass='specialize']")
100+
old_element = setting.find(".//*[@name='Microsoft-Windows-DNS-Client']")
101+
modified = False
102+
if old_element:
103+
setting.remove(old_element)
104+
modified = not dns_content
105+
106+
if dns_content:
107+
dns_component_tree = ET.parse(io.StringIO(dns_content))
108+
new_element = dns_component_tree.getroot()
109+
setting.append(new_element)
110+
modified = True
111+
112+
return setting, modified
113+
27114

28115
def main():
29116
parser = argparse.ArgumentParser(
@@ -55,32 +142,86 @@ def main():
55142
with open(args.var_file, 'r') as f:
56143
data = json.load(f)
57144

58-
modified=0
145+
modified = False
59146
os.chdir(args.build_dir)
60-
unattend=ET.parse(args.unattend_file)
147+
unattend = ET.parse(args.unattend_file)
61148
ET.register_namespace('', "urn:schemas-microsoft-com:unattend")
62149
ET.register_namespace('wcm', "http://schemas.microsoft.com/WMIConfig/2002/State")
63150
ET.register_namespace('xsi', "http://www.w3.org/2001/XMLSchema-instance")
64-
151+
65152
root = unattend.getroot()
66153

67154
if data.get("unattend_timezone"):
68-
modified=1
69-
setting = set_xmlstring(root, ".//*[@pass='oobeSystem']/*[@name='Microsoft-Windows-Shell-Setup']",'{urn:schemas-microsoft-com:unattend}TimeZone', data["unattend_timezone"])
70-
print("windows-ova-unattend: Setting Timezone to %s" % data["unattend_timezone"])
71-
155+
modified = True
156+
setting = set_xmlstring(root, ".//*[@pass='oobeSystem']/*[@name='Microsoft-Windows-Shell-Setup']",
157+
'{urn:schemas-microsoft-com:unattend}TimeZone', data["unattend_timezone"])
158+
print("windows-ova-unattend: Setting Timezone to %s" % data["unattend_timezone"])
159+
72160
admin_password = data.get("admin_password")
73161
if admin_password:
74-
modified=1
75-
set_xmlstring(root, ".//*[@pass='oobeSystem']/*[@name='Microsoft-Windows-Shell-Setup']/{*}UserAccounts/{*}AdministratorPassword",'{urn:schemas-microsoft-com:unattend}Value', admin_password)
76-
set_xmlstring(root, ".//*[@pass='oobeSystem']/*[@name='Microsoft-Windows-Shell-Setup']/{*}AutoLogon/{*}Password",'{urn:schemas-microsoft-com:unattend}Value', admin_password)
77-
print("windows-ova-unattend: Setting Administrator Password")
78-
79-
if modified == 1:
80-
print("windows-ova-unattend: Updating %s ..." % args.unattend_file)
81-
unattend.write(args.unattend_file)
162+
modified = True
163+
set_xmlstring(root,
164+
".//*[@pass='oobeSystem']/*[@name='Microsoft-Windows-Shell-Setup']/{*}UserAccounts/{*}AdministratorPassword",
165+
'{urn:schemas-microsoft-com:unattend}Value', admin_password)
166+
set_xmlstring(root,
167+
".//*[@pass='oobeSystem']/*[@name='Microsoft-Windows-Shell-Setup']/{*}AutoLogon/{*}Password",
168+
'{urn:schemas-microsoft-com:unattend}Value', admin_password)
169+
print("windows-ova-unattend: Setting Administrator Password")
170+
171+
addr_elements = []
172+
ip_addr_cidr = data.get("ipv4_address_cidr")
173+
gateway4 = data.get("gateway4")
174+
if ip_addr_cidr:
175+
modified = True
176+
route_configs = []
177+
if gateway4:
178+
route_configs.append(("0.0.0.0/0", gateway4))
179+
180+
routes = []
181+
for i in range(len(route_configs)):
182+
route_config = route_configs[i]
183+
routes.append(ROUTE_TEMPLATE % {"id": i + 1, "prefix": route_config[0], "gateway": route_config[1]})
184+
print("windows-ova-unattend: Setting Gateway to %s" % route_config[1])
185+
186+
addrs = [ip_addr_cidr]
187+
for i in range(len(addrs)):
188+
addr_elements.append(ADDRESSES_TEMPLATE % {"order": i + 1,
189+
"cidr": addrs[i]})
190+
print("windows-ova-unattend: Setting IP Address to %s" % ip_addr_cidr)
191+
192+
interfaces_content = None
193+
if addr_elements:
194+
interfaces_content = INTERFACE_COMPONENT_TEMPLATE % {"iface_id": INTERFACE_ID,
195+
"addresses": ''.join(addr_elements),
196+
"routes": ''.join(routes)}
197+
198+
setting, xml_modified = ensure_interfaces(root, interfaces_content)
199+
if xml_modified:
200+
modified = True
201+
202+
dns_servers = data.get("dns_servers")
203+
dns_servers_content = ''
204+
if dns_servers:
205+
dns_servers = dns_servers.split()
206+
search_order_content = []
207+
for i in range(len(dns_servers)):
208+
search_order_content.append(DNS_SEARCH_ORDER_TEMPLATE % {"key": i + 1,
209+
"server": dns_servers[i]})
210+
dns_interface_content = [DNS_INTERFACE_TEMPLATE % {"iface_id": INTERFACE_ID,
211+
"dns_search_orders": ''.join(search_order_content)}]
212+
dns_servers_content = DNS_TEMPLATE % {"interfaces": ''.join(dns_interface_content)}
213+
print("windows-ova-unattend: Setting DNS Addresses to %s" % dns_servers)
214+
215+
setting, xml_modified = ensure_dns_settings(root, dns_servers_content)
216+
if xml_modified:
217+
modified = True
218+
219+
if modified:
220+
print("windows-ova-unattend: Updating %s ..." % args.unattend_file)
221+
unattend.write(args.unattend_file)
82222
else:
83-
print("windows-ova-unattend: skipping...")
223+
print("windows-ova-unattend: skipping...")
224+
84225

85226
if __name__ == "__main__":
86227
main()

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",

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)