diff --git a/images/capi/hack/windows-ova-unattend.py b/images/capi/hack/windows-ova-unattend.py
index 818dd8c312..f12863c8f2 100755
--- a/images/capi/hack/windows-ova-unattend.py
+++ b/images/capi/hack/windows-ova-unattend.py
@@ -15,15 +15,102 @@
# limitations under the License.
+import io
import xml.etree.ElementTree as ET
import os
import argparse
import json
+ADDRESSES_TEMPLATE = """ %(cidr)s
+"""
+INTERFACE_COMPONENT_TEMPLATE = """
+
+
+
+
+ false
+
+
+ false
+
+ %(iface_id)s
+
+%(addresses)s
+ %(routes)s
+
+
+
+
+"""
+ROUTE_TEMPLATE = """
+
+ %(id)s
+ %(prefix)s
+ %(gateway)s
+ """
+DNS_TEMPLATE = """
+
+ %(interfaces)s
+
+
+"""
+DNS_INTERFACE_TEMPLATE = """
+
+ %(iface_id)s
+ %(dns_search_orders)s
+
+
+"""
+DNS_SEARCH_ORDER_TEMPLATE = """
+ %(server)s"""
+INTERFACE_ID = "Ethernet0"
+
+
def set_xmlstring(root, location, key, value):
- setting = root.find(location)
- setting.find(key).text = value
- return setting
+ setting = root.find(location)
+ setting.find(key).text = value
+ return setting
+
+
+def ensure_interfaces(root, interfaces_content):
+ setting = root.find("*[@pass='specialize']")
+ old_element = setting.find(".//*[@name='Microsoft-Windows-TCPIP']")
+ modified = False
+ if old_element:
+ setting.remove(old_element)
+ modified = not interfaces_content
+
+ if interfaces_content:
+ interface_component_tree = ET.parse(io.StringIO(interfaces_content))
+ new_element = interface_component_tree.getroot()
+ setting.append(new_element)
+ modified = True
+
+ return setting, modified
+
+
+def ensure_dns_settings(root, dns_content):
+ setting = root.find("*[@pass='specialize']")
+ old_element = setting.find(".//*[@name='Microsoft-Windows-DNS-Client']")
+ modified = False
+ if old_element:
+ setting.remove(old_element)
+ modified = not dns_content
+
+ if dns_content:
+ dns_component_tree = ET.parse(io.StringIO(dns_content))
+ new_element = dns_component_tree.getroot()
+ setting.append(new_element)
+ modified = True
+
+ return setting, modified
+
def main():
parser = argparse.ArgumentParser(
@@ -55,32 +142,86 @@ def main():
with open(args.var_file, 'r') as f:
data = json.load(f)
- modified=0
+ modified = False
os.chdir(args.build_dir)
- unattend=ET.parse(args.unattend_file)
+ unattend = ET.parse(args.unattend_file)
ET.register_namespace('', "urn:schemas-microsoft-com:unattend")
ET.register_namespace('wcm', "http://schemas.microsoft.com/WMIConfig/2002/State")
ET.register_namespace('xsi', "http://www.w3.org/2001/XMLSchema-instance")
-
+
root = unattend.getroot()
if data.get("unattend_timezone"):
- modified=1
- setting = set_xmlstring(root, ".//*[@pass='oobeSystem']/*[@name='Microsoft-Windows-Shell-Setup']",'{urn:schemas-microsoft-com:unattend}TimeZone', data["unattend_timezone"])
- print("windows-ova-unattend: Setting Timezone to %s" % data["unattend_timezone"])
-
+ modified = True
+ setting = set_xmlstring(root, ".//*[@pass='oobeSystem']/*[@name='Microsoft-Windows-Shell-Setup']",
+ '{urn:schemas-microsoft-com:unattend}TimeZone', data["unattend_timezone"])
+ print("windows-ova-unattend: Setting Timezone to %s" % data["unattend_timezone"])
+
admin_password = data.get("admin_password")
if admin_password:
- modified=1
- set_xmlstring(root, ".//*[@pass='oobeSystem']/*[@name='Microsoft-Windows-Shell-Setup']/{*}UserAccounts/{*}AdministratorPassword",'{urn:schemas-microsoft-com:unattend}Value', admin_password)
- set_xmlstring(root, ".//*[@pass='oobeSystem']/*[@name='Microsoft-Windows-Shell-Setup']/{*}AutoLogon/{*}Password",'{urn:schemas-microsoft-com:unattend}Value', admin_password)
- print("windows-ova-unattend: Setting Administrator Password")
-
- if modified == 1:
- print("windows-ova-unattend: Updating %s ..." % args.unattend_file)
- unattend.write(args.unattend_file)
+ modified = True
+ set_xmlstring(root,
+ ".//*[@pass='oobeSystem']/*[@name='Microsoft-Windows-Shell-Setup']/{*}UserAccounts/{*}AdministratorPassword",
+ '{urn:schemas-microsoft-com:unattend}Value', admin_password)
+ set_xmlstring(root,
+ ".//*[@pass='oobeSystem']/*[@name='Microsoft-Windows-Shell-Setup']/{*}AutoLogon/{*}Password",
+ '{urn:schemas-microsoft-com:unattend}Value', admin_password)
+ print("windows-ova-unattend: Setting Administrator Password")
+
+ addr_elements = []
+ ip_addr_cidr = data.get("ipv4_address_cidr")
+ gateway4 = data.get("gateway4")
+ if ip_addr_cidr:
+ modified = True
+ route_configs = []
+ if gateway4:
+ route_configs.append(("0.0.0.0/0", gateway4))
+
+ routes = []
+ for i in range(len(route_configs)):
+ route_config = route_configs[i]
+ routes.append(ROUTE_TEMPLATE % {"id": i + 1, "prefix": route_config[0], "gateway": route_config[1]})
+ print("windows-ova-unattend: Setting Gateway to %s" % route_config[1])
+
+ addrs = [ip_addr_cidr]
+ for i in range(len(addrs)):
+ addr_elements.append(ADDRESSES_TEMPLATE % {"order": i + 1,
+ "cidr": addrs[i]})
+ print("windows-ova-unattend: Setting IP Address to %s" % ip_addr_cidr)
+
+ interfaces_content = None
+ if addr_elements:
+ interfaces_content = INTERFACE_COMPONENT_TEMPLATE % {"iface_id": INTERFACE_ID,
+ "addresses": ''.join(addr_elements),
+ "routes": ''.join(routes)}
+
+ setting, xml_modified = ensure_interfaces(root, interfaces_content)
+ if xml_modified:
+ modified = True
+
+ dns_servers = data.get("dns_servers")
+ dns_servers_content = ''
+ if dns_servers:
+ dns_servers = dns_servers.split()
+ search_order_content = []
+ for i in range(len(dns_servers)):
+ search_order_content.append(DNS_SEARCH_ORDER_TEMPLATE % {"key": i + 1,
+ "server": dns_servers[i]})
+ dns_interface_content = [DNS_INTERFACE_TEMPLATE % {"iface_id": INTERFACE_ID,
+ "dns_search_orders": ''.join(search_order_content)}]
+ dns_servers_content = DNS_TEMPLATE % {"interfaces": ''.join(dns_interface_content)}
+ print("windows-ova-unattend: Setting DNS Addresses to %s" % dns_servers)
+
+ setting, xml_modified = ensure_dns_settings(root, dns_servers_content)
+ if xml_modified:
+ modified = True
+
+ if modified:
+ print("windows-ova-unattend: Updating %s ..." % args.unattend_file)
+ unattend.write(args.unattend_file)
else:
- print("windows-ova-unattend: skipping...")
+ print("windows-ova-unattend: skipping...")
+
if __name__ == "__main__":
main()
diff --git a/images/capi/packer/ova/packer-common.json.tmpl b/images/capi/packer/ova/packer-common.json.tmpl
index 16ea368193..602c88cf7f 100644
--- a/images/capi/packer/ova/packer-common.json.tmpl
+++ b/images/capi/packer/ova/packer-common.json.tmpl
@@ -7,13 +7,16 @@
"disk_controller_type": "pvscsi",
"disk_thin_provisioned": "true",
"disk_type_id": "0",
+ "dns_servers": null,
"firmware": "bios",
"format": "",
+ "gateway4": null,
"guestinfo_datasource_ref": "v1.4.1",
"guestinfo_datasource_script": "{{user `guestinfo_datasource_slug`}}/{{user `guestinfo_datasource_ref`}}/install.sh",
"guestinfo_datasource_slug": "https://raw.githubusercontent.com/vmware/cloud-init-vmware-guestinfo",
"headless": "true",
"insecure_connection": "false",
+ "ipv4_address_cidr": null,
"memory": "8192",
"network": "",
"network_card": "vmxnet3",
diff --git a/images/capi/packer/ova/packer-windows.json b/images/capi/packer/ova/packer-windows.json
index 52eec19894..21aeda6651 100644
--- a/images/capi/packer/ova/packer-windows.json
+++ b/images/capi/packer/ova/packer-windows.json
@@ -1,7 +1,7 @@
{
"builders": [
{
- "content": "{\n \"unattend_timezone\" : \"{{user `unattend_timezone`}}\"\n, \"admin_password\" : \"{{user `windows_admin_password`}}\"\n}",
+ "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}",
"target": "./packer_cache/unattend.json",
"type": "file"
},
@@ -248,10 +248,13 @@
"containerd_version": null,
"disable_hypervisor": null,
"disk_size": "81920",
+ "dns_servers": null,
"firmware": "bios",
+ "gateway4": null,
"http_port_max": "",
"http_port_min": "",
"ib_version": "{{env `IB_VERSION`}}",
+ "ipv4_address_cidr": null,
"kubernetes_base_url": "https://kubernetesreleases.blob.core.windows.net/kubernetes/{{user `kubernetes_semver`}}/binaries/node/windows/{{user `kubernetes_goarch`}}",
"kubernetes_http_package_url": "",
"kubernetes_typed_version": "kube-{{user `kubernetes_semver`}}",