diff --git a/lib/vapi-client-bindings/index.html b/lib/vapi-client-bindings/index.html
index a8662478..b699ae27 100644
--- a/lib/vapi-client-bindings/index.html
+++ b/lib/vapi-client-bindings/index.html
@@ -1 +1 @@
-vapi_client_bindings-3.6.0-py2.py3-none-any.whl
\ No newline at end of file
+vapi_client_bindings-3.7.0-py2.py3-none-any.whl
\ No newline at end of file
diff --git a/lib/vapi-client-bindings/vapi_client_bindings-3.6.0-py2.py3-none-any.whl b/lib/vapi-client-bindings/vapi_client_bindings-3.6.0-py2.py3-none-any.whl
deleted file mode 100644
index 4c5c3ca4..00000000
Binary files a/lib/vapi-client-bindings/vapi_client_bindings-3.6.0-py2.py3-none-any.whl and /dev/null differ
diff --git a/lib/vapi-client-bindings/vapi_client_bindings-3.7.0-py2.py3-none-any.whl b/lib/vapi-client-bindings/vapi_client_bindings-3.7.0-py2.py3-none-any.whl
new file mode 100644
index 00000000..ee2e271f
Binary files /dev/null and b/lib/vapi-client-bindings/vapi_client_bindings-3.7.0-py2.py3-none-any.whl differ
diff --git a/lib/vapi-common-client/index.html b/lib/vapi-common-client/index.html
index 5fda7f65..dfaa19b5 100644
--- a/lib/vapi-common-client/index.html
+++ b/lib/vapi-common-client/index.html
@@ -1 +1 @@
-vapi_common_client-2.25.0-py2.py3-none-any.whl
\ No newline at end of file
+vapi_common_client-2.30.0-py2.py3-none-any.whl
\ No newline at end of file
diff --git a/lib/vapi-common-client/vapi_common_client-2.25.0-py2.py3-none-any.whl b/lib/vapi-common-client/vapi_common_client-2.30.0-py2.py3-none-any.whl
similarity index 59%
rename from lib/vapi-common-client/vapi_common_client-2.25.0-py2.py3-none-any.whl
rename to lib/vapi-common-client/vapi_common_client-2.30.0-py2.py3-none-any.whl
index 49f78fab..3b861a8d 100644
Binary files a/lib/vapi-common-client/vapi_common_client-2.25.0-py2.py3-none-any.whl and b/lib/vapi-common-client/vapi_common_client-2.30.0-py2.py3-none-any.whl differ
diff --git a/lib/vapi-runtime/index.html b/lib/vapi-runtime/index.html
index d8595b75..ffc21dbf 100644
--- a/lib/vapi-runtime/index.html
+++ b/lib/vapi-runtime/index.html
@@ -1 +1 @@
-vapi_runtime-2.25.0-py2.py3-none-any.whl
\ No newline at end of file
+vapi_runtime-2.30.0-py2.py3-none-any.whl
\ No newline at end of file
diff --git a/lib/vapi-runtime/vapi_runtime-2.25.0-py2.py3-none-any.whl b/lib/vapi-runtime/vapi_runtime-2.30.0-py2.py3-none-any.whl
similarity index 81%
rename from lib/vapi-runtime/vapi_runtime-2.25.0-py2.py3-none-any.whl
rename to lib/vapi-runtime/vapi_runtime-2.30.0-py2.py3-none-any.whl
index 42e0b2cf..3ba926aa 100644
Binary files a/lib/vapi-runtime/vapi_runtime-2.25.0-py2.py3-none-any.whl and b/lib/vapi-runtime/vapi_runtime-2.30.0-py2.py3-none-any.whl differ
diff --git a/requirements.txt b/requirements.txt
index 520319ca..72538788 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,6 +1,6 @@
lxml >= 4.3.0
pyVmomi >= 6.7
-vapi-client-bindings == 3.6.0
+vapi-client-bindings == 3.7.0
vmc-client-bindings
nsx-python-sdk
nsx-policy-python-sdk
diff --git a/samples/vsphere/contentlibrary/publishsubscribe/library_publish_subscribe.py b/samples/vsphere/contentlibrary/publishsubscribe/library_publish_subscribe.py
index 680758e6..e86d4ded 100644
--- a/samples/vsphere/contentlibrary/publishsubscribe/library_publish_subscribe.py
+++ b/samples/vsphere/contentlibrary/publishsubscribe/library_publish_subscribe.py
@@ -131,7 +131,7 @@ class LibraryPublishSubscribe(SampleBase):
assert not sub_item.cached, 'Subscribed item must not be cached'
# Force synchronize the subscribed library item to fetch and cache the content
- self.client.subscribed_item_service.sync(sub_item_id, True)
+ self.client.subscribed_item_service.sync(sub_item_id, True, False)
# It is not mandatory to verify sync, it is just for demonstrating the sample workflow.
assert (ClsSyncHelper(self.client, self.SYNC_TIMEOUT_SEC).
verify_item_sync(sub_item_id))
diff --git a/samples/vsphere/vcenter/guest/cloudinitDataCustomizationSpecs.py b/samples/vsphere/vcenter/guest/cloudinitDataCustomizationSpecs.py
new file mode 100644
index 00000000..6161f228
--- /dev/null
+++ b/samples/vsphere/vcenter/guest/cloudinitDataCustomizationSpecs.py
@@ -0,0 +1,193 @@
+#!/usr/bin/env python
+
+"""
+* *******************************************************
+* Copyright (c) VMware, Inc. 2021. All Rights Reserved.
+* SPDX-License-Identifier: MIT
+* *******************************************************
+*
+* DISCLAIMER. THIS PROGRAM IS PROVIDED TO YOU "AS IS" WITHOUT
+* WARRANTIES OR CONDITIONS OF ANY KIND, WHETHER ORAL OR WRITTEN,
+* EXPRESS OR IMPLIED. THE AUTHOR SPECIFICALLY DISCLAIMS ANY IMPLIED
+* WARRANTIES OR CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY,
+* NON-INFRINGEMENT AND FITNESS FOR A PARTICULAR PURPOSE.
+"""
+
+__author__ = 'VMware, Inc.'
+__copyright__ = 'Copyright 2021 VMware, Inc. All rights reserved.'
+__vcenter_version__ = 'VCenter 7.0 U3'
+
+from com.vmware.vcenter.guest_client import CustomizationSpec,\
+ CloudConfiguration, CloudinitConfiguration, ConfigurationSpec,\
+ GlobalDNSSettings
+from pprint import pprint
+from samples.vsphere.common import sample_cli, sample_util
+from samples.vsphere.common.ssl_helper import get_unverified_session
+from vmware.vapi.vsphere.client import create_vsphere_client
+import os
+
+
+class CloudinitDataCustomizationSpecs(object):
+ """
+ Demonstrates create/list/get/set/delete cloud-init data customizationSpecs
+ Sample Prerequisites: 1 vcenter, no ESXi nor VM needed
+ """
+ def __init__(self):
+ self.metadata = None
+ self.userdata = None
+ self.parser = sample_cli.build_arg_parser()
+ self.args = sample_util.process_cli_args(self.parser.parse_args())
+ self.session =\
+ get_unverified_session() if self.args.skipverification else None
+ self.client = create_vsphere_client(server=self.args.server,
+ username=self.args.username,
+ password=self.args.password,
+ session=self.session)
+ self.specs_svc = self.client.vcenter.guest.CustomizationSpecs
+
+ def createCloudinitDataSpec(self, specName, specDesc):
+ """
+ create a cloud-init data customizationSpec
+ """
+ print('------create 1 linux cloud-init data CustomizationSpec-------')
+ cloudinitConfig = CloudinitConfiguration(metadata=self.metadata,
+ userdata=self.userdata)
+ cloudConfig =\
+ CloudConfiguration(cloudinit=cloudinitConfig,
+ type=CloudConfiguration.Type('CLOUDINIT'))
+ configSpec = ConfigurationSpec(cloud_config=cloudConfig)
+ globalDnsSettings = GlobalDNSSettings()
+ adapterMappingList = []
+ customizationSpec =\
+ CustomizationSpec(configuration_spec=configSpec,
+ global_dns_settings=globalDnsSettings,
+ interfaces=adapterMappingList)
+ createSpec = self.specs_svc.CreateSpec(name=specName,
+ description=specDesc,
+ spec=customizationSpec)
+ self.specs_svc.create(spec=createSpec)
+ print('{} has been created'.format(specName))
+ print('-------------------------------------------------------------')
+
+ def createSpecWithMetadataInYamlAndUserdata(self):
+ """
+ create a linux cloud-init data customizationSpec with metadata in yaml
+ format and userdata
+ """
+ metadataYamlFilePath = os.path.join(os.path.dirname(
+ os.path.realpath(__file__)),
+ 'sample_metadata.yaml')
+ userdataFilePath = os.path.join(os.path.dirname(
+ os.path.realpath(__file__)),
+ 'sample_userdata')
+ with open(metadataYamlFilePath, "r") as fp:
+ self.metadata = fp.read().rstrip('\n')
+ with open(userdataFilePath, "r") as fp:
+ self.userdata = fp.read().rstrip('\n')
+ self.createCloudinitDataSpec('specWithMetadataInYamlAndUserdata',
+ 'linux cloud-init data customization spec'
+ 'with metadata in yaml format and '
+ 'userdata')
+
+ def createSpecWithMetadataInJsonAndUserdata(self):
+ """
+ create a linux cloud-init data customizationSpec with metadata in json
+ format and userdata
+ """
+ metadataYamlFilePath = os.path.join(os.path.dirname(
+ os.path.realpath(__file__)),
+ 'sample_metadata.json')
+ userdataFilePath = os.path.join(os.path.dirname(
+ os.path.realpath(__file__)),
+ 'sample_userdata')
+ with open(metadataYamlFilePath, "r") as fp:
+ self.metadata = fp.read().rstrip('\n')
+ with open(userdataFilePath, "r") as fp:
+ self.userdata = fp.read().rstrip('\n')
+ self.createCloudinitDataSpec('specWithMetadataInJsonAndUserdata',
+ 'linux cloud-init data customization spec'
+ 'with metadata in json format and '
+ 'userdata')
+
+ def createSpecWithMetadataOnly(self):
+ """
+ create a linux cloud-init data customizationSpec with metadata in yaml
+ format and without userdata
+ """
+ metadataYamlFilePath = os.path.join(os.path.dirname(
+ os.path.realpath(__file__)),
+ 'sample_metadata.yaml')
+ with open(metadataYamlFilePath, "r") as fp:
+ self.metadata = fp.read().rstrip('\n')
+ self.createCloudinitDataSpec('specWithMetadataOnly',
+ 'linux cloud-init data customization spec'
+ 'with metadata only')
+
+ def listCustomizationSpecs(self):
+ print('------list all existing customization Spec------')
+ existingSpecs = self.specs_svc.list()
+ if (len(existingSpecs) > 0):
+ pprint(existingSpecs)
+ else:
+ print('no specs found')
+ print('------------------------------------------------')
+
+ def getSetCloudinitDataCustomizationSpec(self):
+ print('------get an existing cloud-init data customization Spec------')
+ existingSpecs = self.specs_svc.list()
+ if (len(existingSpecs) > 0):
+ existingSpecName = existingSpecs[0].name
+ existingSpec = self.specs_svc.get(existingSpecName)
+ pprint(existingSpec)
+ # Set a new spec description
+ newSpecDesc =\
+ '{} updated by vapi set() method'.format(existingSpecName)
+ existingSpec.spec.description = newSpecDesc
+ # Set a new metadata
+ metadata =\
+ """
+ instance-id: test-vm-id-01-updated
+ local-hostname: test-vm-01-updated
+ network:
+ version: 2
+ ethernets:
+ ens160:
+ match:
+ name: ens*
+ dhcp4: yes
+ """
+ existingSpec.spec.spec.configuration_spec.cloud_config.cloudinit.\
+ metadata = metadata
+ print('------set existing cloud-init data customizationSpec------')
+ self.specs_svc.set(name=existingSpecName, spec=existingSpec.spec)
+ print('{} has been set'.format(existingSpecName))
+ pprint(existingSpec)
+ else:
+ print('no specs found')
+ print('-------------------------------------------------------------')
+
+ def deleteCustomizationSpecs(self):
+ print('-----delete existing customization Specs-----')
+ existingSpecs = self.specs_svc.list()
+ if (len(existingSpecs) > 0):
+ for i in range(len(existingSpecs)):
+ specName = existingSpecs[i].name
+ self.specs_svc.delete(specName)
+ print('{} has been deleted'.format(specName))
+ else:
+ print('no specs found')
+ print('-------------------------------------------------------------')
+
+
+def main():
+ cloudinitDataCustomizationSpecs = CloudinitDataCustomizationSpecs()
+ cloudinitDataCustomizationSpecs.createSpecWithMetadataInYamlAndUserdata()
+ cloudinitDataCustomizationSpecs.createSpecWithMetadataInJsonAndUserdata()
+ cloudinitDataCustomizationSpecs.createSpecWithMetadataOnly()
+ cloudinitDataCustomizationSpecs.listCustomizationSpecs()
+ cloudinitDataCustomizationSpecs.getSetCloudinitDataCustomizationSpec()
+ cloudinitDataCustomizationSpecs.deleteCustomizationSpecs()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/samples/vsphere/vcenter/guest/customizationSpecs.py b/samples/vsphere/vcenter/guest/customizationSpecs.py
new file mode 100644
index 00000000..d486b35f
--- /dev/null
+++ b/samples/vsphere/vcenter/guest/customizationSpecs.py
@@ -0,0 +1,387 @@
+#!/usr/bin/env python
+
+"""
+* *******************************************************
+* Copyright (c) VMware, Inc. 2020. All Rights Reserved.
+* SPDX-License-Identifier: MIT
+* *******************************************************
+*
+* DISCLAIMER. THIS PROGRAM IS PROVIDED TO YOU "AS IS" WITHOUT
+* WARRANTIES OR CONDITIONS OF ANY KIND, WHETHER ORAL OR WRITTEN,
+* EXPRESS OR IMPLIED. THE AUTHOR SPECIFICALLY DISCLAIMS ANY IMPLIED
+* WARRANTIES OR CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY,
+* NON-INFRINGEMENT AND FITNESS FOR A PARTICULAR PURPOSE.
+"""
+
+__author__ = 'VMware, Inc.'
+__copyright__ = 'Copyright 2020 VMware, Inc. All rights reserved.'
+__vcenter_version__ = '7.0+'
+
+from pprint import pprint
+import configparser
+import os
+
+from vmware.vapi.vsphere.client import create_vsphere_client
+from samples.vsphere.common import sample_cli
+from samples.vsphere.common import sample_util
+from samples.vsphere.common.ssl_helper import get_unverified_session
+
+from com.vmware.vcenter.guest_client import (CustomizationSpec,
+ HostnameGenerator,
+ LinuxConfiguration,
+ ConfigurationSpec,
+ GlobalDNSSettings,
+ AdapterMapping,
+ IPSettings,
+ Ipv4,
+ Ipv6,
+ Ipv6Address)
+from com.vmware.vcenter.guest_client import (WindowsConfiguration,
+ WindowsSysprep,
+ Domain,
+ GuiUnattended,
+ UserData,
+ WindowsNetworkAdapterSettings)
+
+
+class CustomizationSpecManager(object):
+ """
+ Demonstrates create/list/get/set/delete customizationSpecs in vCenter
+ Sample Prerequisites: 1 vcenter, no ESXi nor VM needed
+ """
+
+ def __init__(self):
+ parser = sample_cli.build_arg_parser()
+ parser.add_argument('-x', '--win_password',
+ action='store',
+ help='windows admin password to be customized')
+ args = sample_util.process_cli_args(parser.parse_args())
+ if args.win_password:
+ self.win_password = args.win_password
+ else:
+ self.win_password = None
+ session = get_unverified_session() if args.skipverification else None
+ self.client = create_vsphere_client(server=args.server,
+ username=args.username,
+ password=args.password,
+ session=session)
+ self.specs_svc = self.client.vcenter.guest.CustomizationSpecs
+ # get customization config
+ self.config = configparser.ConfigParser()
+ self.linCfgPath = os.path.join(os.path.dirname(
+ os.path.realpath(__file__)),
+ 'linSpec.cfg')
+ self.winCfgPath = os.path.join(os.path.dirname(
+ os.path.realpath(__file__)),
+ 'winSpec.cfg')
+ self.specsAdded = []
+
+ # common method to parse specInfo for linux/windows spec
+ def parseSpecInfo(self):
+ self.specName = self.config['CREATESPEC']['specName']
+ self.specDesc = self.config['CREATESPEC']['specDesc']
+
+ # common method to parse network cfg for linux/windows spec
+ def parseNetwork(self):
+ # parse macAddress
+ self.macAddress = self.config['NETWORK'].get('macAddress')
+ # parse ipv4
+ self.ipv4Type = self.config['NETWORK'].get('ipv4Type', 'DHCP')
+ if self.ipv4Type == 'STATIC':
+ self.ipv4_prefix = self.config['NETWORK'].getint('ipv4_prefix')
+ self.ipv4_gateways = self.config['NETWORK'].get('ipv4_gateways')
+ if self.ipv4_gateways is not None:
+ self.ipv4_gateways = self.ipv4_gateways.split(',')
+ self.ipv4_ip = self.config['NETWORK'].get('ipv4_ip')
+ elif (self.ipv4Type == 'DHCP' or
+ self.ipv4Type == 'USER_INPUT_REQUIRED'):
+ self.ipv4_prefix = None
+ self.ipv4_gateways = None
+ self.ipv4_ip = None
+ else:
+ raise Exception('Wrong ipv4Type "{}"'.format(self.ipv4Type))
+ # parse ipv6
+ self.ipv6Type = self.config['NETWORK'].get('ipv6Type')
+ if self.ipv6Type == 'STATIC':
+ self.ipv6_prefix = self.config['NETWORK'].getint('ipv6_prefix')
+ self.ipv6_gateways = self.config['NETWORK'].get('ipv6_gateways')
+ if self.ipv6_gateways is not None:
+ self.ipv6_gateways = self.ipv6_gateways.split(',')
+ self.ipv6_ip = self.config['NETWORK'].get('ipv6_ip')
+ elif ((self.ipv6Type is None) or (self.ipv4Type == 'DHCP') or
+ (self.ipv4Type == 'USER_INPUT_REQUIRED')):
+ self.ipv6_prefix = None
+ self.ipv6_ip = None
+ self.ipv6_gateways = None
+ else:
+ raise Exception('Wrong ipv6Type "{}"'.format(self.ipv6Type))
+
+ # common method to parse hostname cfg for linux/windows spec
+ def parseHostname(self):
+ # parse hostname generator type
+ self.hostnameType =\
+ self.config['HOSTNAME'].get('hostnameGeneratorType',
+ 'VIRTUAL_MACHINE')
+ if (self.hostnameType == 'VIRTUAL_MACHINE' or
+ self.hostnameType == 'USER_INPUT_REQUIRED'):
+ self.prefix = None
+ self.fixedName = None
+ elif self.hostnameType == 'PREFIX':
+ self.prefix = self.config['HOSTNAME'].get('prefix')
+ self.fixedName = None
+ elif self.hostnameType == 'FIXED':
+ self.fixedName = self.config['HOSTNAME'].get('fixedName')
+ self.prefix = None
+ else:
+ raise Exception('Wrong hostnameGeneratorType "{}"'.format(
+ self.hostnameType))
+
+ # common method to parse DNS cfg for linux/windows spec
+ def parseDns(self):
+ self.globalDnsServers = self.config['DNS'].get('dnsServers')
+ if self.globalDnsServers is not None:
+ self.globalDnsServers = self.globalDnsServers.split(',')
+ self.globalDnsSuffixs = self.config['DNS'].get('dnsSuffixs')
+ if self.globalDnsSuffixs is not None:
+ self.globalDnsSuffixs = self.globalDnsSuffixs.split(',')
+
+ def parseWinnics(self):
+ self.netBiosMode = self.config['WINNICS'].get('netBiosMode')
+ self.dnsServers = self.config['WINNICS'].get('dnsServers')
+ if self.dnsServers is not None:
+ self.dnsServers = self.dnsServers.split(',')
+ self.dnsDomain = self.config['WINNICS'].get('dnsDomain')
+ self.winsServers = self.config['WINNICS'].get('winsServers')
+ if self.winsServers is not None:
+ self.winsServers = self.winsServers.split(',')
+
+ def parseLinuxCfg(self):
+ self.config.read(self.linCfgPath)
+ self.parseSpecInfo()
+ self.linSpecName = self.specName
+ self.parseNetwork()
+ self.parseHostname()
+ self.domainName = self.config['LINUXCONFIG'].get('domainName')
+ self.timezone = self.config['LINUXCONFIG'].get('timezone')
+ self.script_text = self.config['LINUXCONFIG'].get('script_text')
+ self.parseDns()
+
+ def parseWinCfg(self):
+ self.config.read(self.winCfgPath)
+ self.parseSpecInfo()
+ self.winSpecName = self.specName
+ self.parseNetwork()
+ self.parseHostname()
+ self.parseDns()
+ self.rebootOption = self.config['WINCONFIG'].get('rebootOption')
+ self.fullName = self.config['WINCONFIG'].get('fullName')
+ self.org = self.config['WINCONFIG'].get('organization')
+ self.productKey = self.config['WINCONFIG'].get('productKey')
+ # parse domain or workgroup
+ self.domainType =\
+ self.config['WINCONFIG'].get('domainType', 'WORKGROUP')
+ if self.domainType == "WORKGROUP":
+ self.workgroup = self.config['WINCONFIG'].get('workgroup')
+ self.domain = None
+ self.domainUser = None
+ self.domainPass = None
+ elif self.domainType == "DOMAIN":
+ self.workgroup = None
+ self.domain = self.config['WINCONFIG'].get('domain')
+ self.domainUser = self.config['WINCONFIG'].get('domainUser')
+ self.domainPass = self.config['WINCONFIG'].get('domainPass')
+ else:
+ raise Exception('Wrong domainType "{}"'.format(self.domainType))
+ self.autoLogon =\
+ self.config['WINCONFIG'].getboolean('autoLogon', False)
+ self.autoLogonCount =\
+ self.config['WINCONFIG'].getint('autoLogonCount', 0)
+ self.timezone = self.config['WINCONFIG'].getint('timezone', 4)
+ # The local Admin password
+ # ### WARNING: USE CLEAR TEXT IN winSpec.cfg AT YOUR OWN RISK!!! ###
+ # Suggested to use "--win_password" command line option instead
+ # It will overrid this value here
+ if not self.win_password:
+ self.win_password = self.config['WINCONFIG'].get('password')
+ self.gui_run_once_commands =\
+ self.config['WINCONFIG'].get('gui_run_once_commands')
+ if self.gui_run_once_commands is not None:
+ self.gui_run_once_commands = self.gui_run_once_commands.split(',')
+ self.sysprepXml = self.config['WINCONFIG'].get('sysprepXml')
+ self.parseWinnics()
+
+ def listCustomizationSpecs(self):
+ """
+ List CustomizationSpecs present in vc server
+ """
+ print("------------list--------------")
+ print("List Of CustomizationSpecs:")
+ list_of_specs = self.specs_svc.list()
+ self.specCount = len(list_of_specs)
+ pprint(list_of_specs)
+
+ def createLinuxSpec(self):
+ print("------------create 1 linux Customizationpec----------------")
+ self.parseLinuxCfg()
+ computerName = HostnameGenerator(prefix=self.prefix,
+ fixed_name=self.fixedName,
+ type=HostnameGenerator.Type(
+ self.hostnameType))
+ spec_linuxConfig = LinuxConfiguration(domain=self.domainName,
+ hostname=computerName,
+ time_zone=self.timezone,
+ script_text=self.script_text)
+ spec_configSpec = ConfigurationSpec(linux_config=spec_linuxConfig)
+ # AdapterMapping
+ ipv4Cfg = Ipv4(type=Ipv4.Type(self.ipv4Type), prefix=self.ipv4_prefix,
+ gateways=self.ipv4_gateways, ip_address=self.ipv4_ip)
+ if self.ipv6Type is not None:
+ ipv6addr = [Ipv6Address(prefix=self.ipv6_prefix,
+ ip_address=self.ipv6_ip)]
+ ipv6Cfg = Ipv6(gateways=self.ipv6_gateways, ipv6=ipv6addr,
+ type=Ipv6.Type(self.ipv6Type))
+ else:
+ ipv6Cfg = None
+ ipSettings = IPSettings(windows=None, ipv4=ipv4Cfg, ipv6=ipv6Cfg)
+ adapterMappingList = [AdapterMapping(adapter=ipSettings,
+ mac_address=self.macAddress)]
+ # dns_settings
+ dns_settings = GlobalDNSSettings(dns_servers=self.globalDnsServers,
+ dns_suffix_list=self.globalDnsSuffixs)
+ # CreateSpec
+ linspec_spec = CustomizationSpec(configuration_spec=spec_configSpec,
+ interfaces=adapterMappingList,
+ global_dns_settings=dns_settings)
+ lin_create_spec = self.specs_svc.CreateSpec(name=self.specName,
+ description=self.specDesc,
+ spec=linspec_spec)
+ # svc Create
+ self.specs_svc.create(spec=lin_create_spec)
+ # append it to existing list, for delete and cleanup
+ self.specsAdded.append(self.specName)
+ # list after create
+ self.listCustomizationSpecs()
+ print("----------------------------")
+
+ def createWinSpec(self):
+ print("------------create 1 windows CustomizationSpec----------------")
+ self.parseWinCfg()
+ # IPSettings
+ ipv4Cfg = Ipv4(type=Ipv4.Type(self.ipv4Type), prefix=self.ipv4_prefix,
+ gateways=self.ipv4_gateways, ip_address=self.ipv4_ip)
+ if self.ipv6Type is not None:
+ ipv6addr = [Ipv6Address(prefix=self.ipv6_prefix,
+ ip_address=self.ipv6_ip)]
+ ipv6Cfg = Ipv6(gateways=self.ipv6_gateways, ipv6=ipv6addr,
+ type=Ipv6.Type(self.ipv6Type))
+ else:
+ ipv6Cfg = None
+ windowsNicSettings = WindowsNetworkAdapterSettings(
+ net_bios_mode=self.netBiosMode, dns_servers=self.dnsServers,
+ dns_domain=self.dnsDomain, wins_servers=self.winsServers)
+ ipSettings = IPSettings(windows=windowsNicSettings,
+ ipv4=ipv4Cfg, ipv6=ipv6Cfg)
+ # AdapterMapping
+ adapterMappingList = [AdapterMapping(adapter=ipSettings,
+ mac_address=self.macAddress)]
+
+ # WindowsConfiguration
+ myReboot = WindowsConfiguration.RebootOption(self.rebootOption)
+ computerName = HostnameGenerator(prefix=self.prefix,
+ fixed_name=self.fixedName,
+ type=HostnameGenerator.Type(
+ self.hostnameType))
+ userData = UserData(computer_name=computerName,
+ product_key=self.productKey,
+ full_name=self.fullName,
+ organization=self.org)
+ myDomain = Domain(domain=self.domain, workgroup=self.workgroup,
+ domain_username=self.domainUser,
+ domain_password=self.domainPass,
+ type=Domain.Type(self.domainType))
+ guiUnattended = GuiUnattended(auto_logon_count=self.autoLogonCount,
+ auto_logon=self.autoLogon,
+ time_zone=self.timezone,
+ password=self.win_password)
+ mySysprep = WindowsSysprep(domain=myDomain,
+ gui_unattended=guiUnattended,
+ user_data=userData,
+ gui_run_once_commands=self.
+ gui_run_once_commands)
+ winCfg = WindowsConfiguration(reboot=myReboot,
+ sysprep=mySysprep,
+ sysprep_xml=self.sysprepXml)
+ # CustomizationSpec
+ spec_configSpec = ConfigurationSpec(windows_config=winCfg)
+ dns_settings = GlobalDNSSettings(dns_servers=self.globalDnsServers,
+ dns_suffix_list=self.globalDnsSuffixs)
+ winspec_spec = CustomizationSpec(configuration_spec=spec_configSpec,
+ interfaces=adapterMappingList,
+ global_dns_settings=dns_settings)
+ # CreateSpec
+ win_create_spec = self.specs_svc.CreateSpec(name=self.specName,
+ description=self.specDesc,
+ spec=winspec_spec)
+ # list before create
+ self.listCustomizationSpecs()
+ existingSpecCount = self.specCount
+ # svc Create
+ self.specs_svc.create(spec=win_create_spec)
+ # append it to existing list, for delete and cleanup
+ self.specsAdded.append(self.specName)
+ # list after create
+ self.listCustomizationSpecs()
+ print("----------------------------")
+ newSpecCount = self.specCount
+ if(newSpecCount != existingSpecCount + 1):
+ raise Exception('Error createSpec due to spec count={}'.
+ format(newSpecCount))
+
+ def getSetSpec(self):
+ print("-----------Get existing Spec------------")
+ # Get created specs, modify timezone and description of the linSpec
+ linSpec = self.specs_svc.get(self.linSpecName)
+ pprint(linSpec)
+ winSpec = self.specs_svc.get(self.winSpecName)
+ pprint(winSpec)
+ linSpec.spec.spec.configuration_spec.linux_config.time_zone =\
+ 'Europe/London'
+ linSpec.spec.description = linSpec.spec.description +\
+ " modified by vapi set() method"
+ print("-----------Set to modify existing linSpec------------")
+ self.specs_svc.set(name=self.linSpecName, spec=linSpec.spec)
+ # Now check again if its timezone and description has changed
+ print("-----------Get the modified linSpec------------")
+ linSpec = self.specs_svc.get(self.linSpecName)
+ pprint(linSpec)
+ print("----------------------------")
+
+ def deleteSpec(self):
+ print("-----------Delete created spec for cleanup------------")
+ print("-----------before delete------------")
+ self.listCustomizationSpecs()
+ existingSpecCount = self.specCount
+ for specName in self.specsAdded:
+ self.specs_svc.delete(specName)
+ existingSpecCount -= 1
+ # list again, there should be []
+ print("-----------after delete------------")
+ self.listCustomizationSpecs()
+ newSpecCount = self.specCount
+ print("----------------------------")
+ if(newSpecCount != existingSpecCount):
+ raise Exception('Error deleteSpec due to current specCount {}!={}'.
+ format(newSpecCount, existingSpecCount))
+
+
+def main():
+ myCustSpecMgr = CustomizationSpecManager()
+ myCustSpecMgr.listCustomizationSpecs()
+ myCustSpecMgr.createLinuxSpec()
+ myCustSpecMgr.createWinSpec()
+ myCustSpecMgr.getSetSpec()
+ myCustSpecMgr.deleteSpec()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/samples/vsphere/vcenter/guest/customizationSpecs_import_export.py b/samples/vsphere/vcenter/guest/customizationSpecs_import_export.py
new file mode 100644
index 00000000..15a85b08
--- /dev/null
+++ b/samples/vsphere/vcenter/guest/customizationSpecs_import_export.py
@@ -0,0 +1,102 @@
+#!/usr/bin/env python
+
+"""
+* *******************************************************
+* Copyright (c) VMware, Inc. 2020. All Rights Reserved.
+* SPDX-License-Identifier: MIT
+* *******************************************************
+*
+* DISCLAIMER. THIS PROGRAM IS PROVIDED TO YOU "AS IS" WITHOUT
+* WARRANTIES OR CONDITIONS OF ANY KIND, WHETHER ORAL OR WRITTEN,
+* EXPRESS OR IMPLIED. THE AUTHOR SPECIFICALLY DISCLAIMS ANY IMPLIED
+* WARRANTIES OR CONDITIONS OF MERCHANTABILITY, SATISFACTORY QUALITY,
+* NON-INFRINGEMENT AND FITNESS FOR A PARTICULAR PURPOSE.
+"""
+
+__author__ = 'VMware, Inc.'
+__copyright__ = 'Copyright 2020 VMware, Inc. All rights reserved.'
+__vcenter_version__ = '7.0+'
+
+from pprint import pprint
+import os
+
+from vmware.vapi.vsphere.client import create_vsphere_client
+from samples.vsphere.common import sample_cli
+from samples.vsphere.common import sample_util
+from samples.vsphere.common.ssl_helper import get_unverified_session
+
+
+class CustomizationSpecManager(object):
+ """
+ Demonstrates import/export customizationSpecs in vCenter
+ Sample Prerequisites: 1 vcenter, no ESXi nor VM needed
+ """
+
+ def __init__(self):
+ parser = sample_cli.build_arg_parser()
+ args = sample_util.process_cli_args(parser.parse_args())
+ session = get_unverified_session() if args.skipverification else None
+ self.client = create_vsphere_client(server=args.server,
+ username=args.username,
+ password=args.password,
+ session=session)
+ self.specs_svc = self.client.vcenter.guest.CustomizationSpecs
+ filePath = os.path.join(os.path.dirname(os.path.realpath(__file__)),
+ 'sample_import.json')
+ with open(filePath, 'r') as f:
+ self.jsonDataRaw = f.read()
+ self.specName = 'defaultCustSpec01'
+ self.specsAdded = []
+
+ def listCustomizationSpecs(self):
+ """
+ List CustomizationSpecs present in vc server
+ """
+ print("------------list--------------")
+ print("List Of CustomizationSpecs:")
+ list_of_specs = self.specs_svc.list()
+ pprint(list_of_specs)
+
+ def importSpecTest(self):
+ print("--------import and create Customizationpec from json--------")
+ # CreateSpec
+ create_spec = self.specs_svc.import_specification(self.jsonDataRaw)
+ # svc Create
+ self.specs_svc.create(spec=create_spec)
+ # append it to existing list, for delete and cleanup
+ self.specsAdded.append(self.specName)
+ # list after create
+ self.listCustomizationSpecs()
+ print("----------------------------")
+
+ def exportSpecTest(self):
+ print("-----------Get existing Spec------------")
+ # Get a spec, modify its timezone and description
+ mySpecXml = self.specs_svc.export(self.specName, 'XML')
+ pprint(mySpecXml)
+ mySpecJson = self.specs_svc.export(self.specName, 'JSON')
+ pprint(mySpecJson)
+ print("----------------------------")
+
+ def deleteSpecTest(self):
+ print("-----------Delete created spec for cleanup------------")
+ print("-----------before delete------------")
+ self.listCustomizationSpecs()
+ for specName in self.specsAdded:
+ self.specs_svc.delete(specName)
+ # list again, there should be []
+ print("-----------after delete------------")
+ self.listCustomizationSpecs()
+ print("----------------------------")
+
+
+def main():
+ myCustSpecMgr = CustomizationSpecManager()
+ myCustSpecMgr.listCustomizationSpecs()
+ myCustSpecMgr.importSpecTest()
+ myCustSpecMgr.exportSpecTest()
+ myCustSpecMgr.deleteSpecTest()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/samples/vsphere/vcenter/guest/linSpec.cfg b/samples/vsphere/vcenter/guest/linSpec.cfg
new file mode 100644
index 00000000..bec93763
--- /dev/null
+++ b/samples/vsphere/vcenter/guest/linSpec.cfg
@@ -0,0 +1,51 @@
+[CREATESPEC]
+specName=testCreateLinSpec1
+specDesc=This is a linux customizationSpec created by vAPI
+
+[HOSTNAME]
+# valid types are: FIXED, PREFIX, VIRTUAL_MACHINE, USER_INPUT_REQUIRED
+hostnameGeneratorType=VIRTUAL_MACHINE
+# If hostnameGeneratorType is "PREFIX", prefix must be set to some string
+#prefix=
+# If hostnameGeneratorType is "FIXED", fixedName must be set to the hostname string
+#fixedName=
+
+[LINUXCONFIG]
+domainName=test.abc.com
+# valid timezone list: https://kb.vmware.com/s/article/2145518
+timezone=Asia/Shanghai
+# should be some valid bash/shell script txt or empty
+#scriptText=
+
+
+[NETWORK]
+### MacAddress ###
+# macAddress is optional
+# macAddress=fc:00:0a:33:22:11
+
+### IPV4 ###
+# valid types are: DHCP, STATIC, USER_INPUT_REQUIRED.
+# If use "STATIC", then must also input "prefix, gateways=None, ip_address="
+## sample DHCP ipv4Type ##
+ipv4Type=DHCP
+## sample STATIC ipv4Type ##
+#ipv4Type=STATIC
+#ipv4_prefix=31
+#ipv4_gateways=192.168.11.13
+#ipv4_ip=192.168.11.1
+
+### IPV6 ###
+# valid types are: DHCP, STATIC, USER_INPUT_REQUIRED.
+# If use "STATIC", then must also input "prefix, gateways=None, ip_address="
+## sample DHCP ipv6Type ##
+#ipv6Type=DHCP
+## sample STATIC ipv6Type ##
+#ipv6Type=STATIC
+#ipv6_prefix=128
+#ipv6_gateways=fc00:10:31:11::1
+#ipv6_ip=fc00:10:31:11::34
+
+
+[DNS]
+dnsServers=10.1.2.3,8.8.8.8
+dnsSuffixs=test.abc.com,test2.com
diff --git a/samples/vsphere/vcenter/guest/sample_import.json b/samples/vsphere/vcenter/guest/sample_import.json
new file mode 100644
index 00000000..df1b1f7a
--- /dev/null
+++ b/samples/vsphere/vcenter/guest/sample_import.json
@@ -0,0 +1,106 @@
+{
+ "STRUCTURE": {
+ "com.vmware.vcenter.guest.customization_specs.spec": {
+ "description": "This is a generic ipv4 customizationSpec for linux VM customization. Created by VIM API",
+ "name": "defaultCustSpec01",
+ "spec": {
+ "STRUCTURE": {
+ "com.vmware.vcenter.guest.customization_spec": {
+ "configuration_spec": {
+ "STRUCTURE": {
+ "com.vmware.vcenter.guest.configuration_spec": {
+ "linux_config": {
+ "OPTIONAL": {
+ "STRUCTURE": {
+ "com.vmware.vcenter.guest.linux_configuration": {
+ "domain": "VMWare.com",
+ "hostname": {
+ "STRUCTURE": {
+ "com.vmware.vcenter.guest.hostname_generator": {
+ "fixed_name": {
+ "OPTIONAL": "vapiCustName123"
+ },
+ "prefix": {
+ "OPTIONAL": null
+ },
+ "type": "FIXED"
+ }
+ }
+ },
+ "time_zone": {
+ "OPTIONAL": null
+ },
+ "script_text": {
+ "OPTIONAL": null
+ }
+ }
+ }
+ }
+ },
+ "windows_config": {
+ "OPTIONAL": null
+ }
+ }
+ }
+ },
+ "global_DNS_settings": {
+ "STRUCTURE": {
+ "com.vmware.vcenter.guest.global_DNS_settings": {
+ "dns_servers": {
+ "OPTIONAL": []
+ },
+ "dns_suffix_list": {
+ "OPTIONAL": []
+ }
+ }
+ }
+ },
+ "interfaces": [
+ {
+ "STRUCTURE": {
+ "com.vmware.vcenter.guest.adapter_mapping": {
+ "adapter": {
+ "STRUCTURE": {
+ "com.vmware.vcenter.guest.IP_settings": {
+ "ipv4": {
+ "OPTIONAL": {
+ "STRUCTURE": {
+ "com.vmware.vcenter.guest.ipv4": {
+ "gateways": {
+ "OPTIONAL": [
+ "192.168.12.1"
+ ]
+ },
+ "ip_address": {
+ "OPTIONAL": "192.168.12.11"
+ },
+ "prefix": {
+ "OPTIONAL": 31
+ },
+ "type": "STATIC"
+ }
+ }
+ }
+ },
+ "ipv6": {
+ "OPTIONAL": null
+ },
+ "windows": {
+ "OPTIONAL": null
+ }
+ }
+ }
+ },
+ "mac_address": {
+ "OPTIONAL": null
+ }
+ }
+ }
+ }
+ ]
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/samples/vsphere/vcenter/guest/sample_metadata.json b/samples/vsphere/vcenter/guest/sample_metadata.json
new file mode 100644
index 00000000..a579202a
--- /dev/null
+++ b/samples/vsphere/vcenter/guest/sample_metadata.json
@@ -0,0 +1,21 @@
+{
+ "local-hostname": "test-vm-01",
+ "instance-id": "test-vm-id-01",
+ "network": {
+ "version":2,
+ "ethernets": {
+ "nics": {
+ "match": {
+ "name": "eth*"
+ },
+ "nameservers": {
+ "addresses": ["127.0.0.53"],
+ "search": ["eng.vmware.com", "vmware.com"]
+ },
+ "gateway4": "10.182.15.255",
+ "dhcp4": false,
+ "addresses": ["10.182.12.25/24"]
+ }
+ }
+ }
+}
diff --git a/samples/vsphere/vcenter/guest/sample_metadata.yaml b/samples/vsphere/vcenter/guest/sample_metadata.yaml
new file mode 100644
index 00000000..87947441
--- /dev/null
+++ b/samples/vsphere/vcenter/guest/sample_metadata.yaml
@@ -0,0 +1,9 @@
+instance-id: test-vm-id-01
+local-hostname: test-vm-01
+network:
+ version: 2
+ ethernets:
+ nics:
+ match:
+ name: eth*
+ dhcp4: yes
diff --git a/samples/vsphere/vcenter/guest/sample_userdata b/samples/vsphere/vcenter/guest/sample_userdata
new file mode 100644
index 00000000..209c6494
--- /dev/null
+++ b/samples/vsphere/vcenter/guest/sample_userdata
@@ -0,0 +1,9 @@
+#cloud-config
+# See more userdata examples at:
+# https://cloudinit.readthedocs.io/en/latest/topics/examples.html
+write_files:
+- encoding: gzip
+ content: !!binary |
+ H4sIAIDb/U8C/1NW1E/KzNMvzuBKTc7IV8hIzcnJVyjPL8pJ4QIA6N+MVxsAAAA=
+ path: /usr/bin/hello
+ permissions: '0755'
diff --git a/samples/vsphere/vcenter/guest/winSpec.cfg b/samples/vsphere/vcenter/guest/winSpec.cfg
new file mode 100644
index 00000000..81a12bed
--- /dev/null
+++ b/samples/vsphere/vcenter/guest/winSpec.cfg
@@ -0,0 +1,87 @@
+[CREATESPEC]
+specName=testWinSpec1
+specDesc=This is a windows customizationSpec created by vAPI
+
+[HOSTNAME]
+# valid types are: FIXED, PREFIX, VIRTUAL_MACHINE, USER_INPUT_REQUIRED
+hostnameGeneratorType=VIRTUAL_MACHINE
+# If hostnameGeneratorType is "PREFIX", prefix must be set to some string
+#prefix=''
+# If hostnameGeneratorType is "FIXED", fixedName must be set to the hostname string
+#fixedName=''
+
+[WINCONFIG]
+# valid options are: REBOOT, NO_REBOOT, SHUTDOWN
+rebootOption=REBOOT
+productKey=
+fullName=AComputerNameFull
+organization=testCome
+
+# domain or worgroup, valid type are: WORKGROUP, DOMAIN
+domainType=WORKGROUP
+# if type is "WORKGROUP", workgroup field must be set to some workgroup name str #
+workgroup=WORKGROUP
+# if type is "DOMAIN", domain name, domainUser, domainPassword should be set #
+#domain=
+#domainUser=
+#domainPass=
+
+# valid timezone list: https://support.microsoft.com/en-us/help/973627/microsoft-time-zone-index-values
+# "2" means "(GMT-10:00) Hawaii
+# "4" means "(GMT-08:00) Pacific Time (US and Canada); Tijuana"
+# "D2" means "(GMT+08:00) Beijing, Chongqing, Hong Kong, Urumqi"
+timezone=2
+
+# auto logon after customization, if autoLogon is True, then admin password must be set
+autoLogonCount=0
+autoLogon=False
+
+# The local Admin password
+### WARNING: USE CLEAR TEXT HERE AT YOUR OWN RISK!!! ###
+### Suggested to use "--win_password" command line option instead
+### It will overrid this value here
+# password=ASecurePass!
+
+# optional, TBD
+# gui_run_once_commands=
+# optional, TBD
+# sysprepXml=
+
+[NETWORK]
+### MacAddress ###
+# macAddress is optional
+# macAddress=fc:00:0a:33:22:11
+
+### IPV4 ###
+# valid types are: DHCP, STATIC, USER_INPUT_REQUIRED.
+# If use "STATIC", then must also input "prefix, gateways=None, ip_address="
+## sample DHCP ipv4Type ##
+#ipv4Type=DHCP
+## sample STATIC ipv4Type ##
+ipv4Type=STATIC
+ipv4_prefix=31
+ipv4_gateways=192.168.11.13
+ipv4_ip=192.168.11.1
+
+### IPV6 ###
+# valid types are: DHCP, STATIC, USER_INPUT_REQUIRED.
+# If use "STATIC", then must also input "prefix, gateways=None, ip_address="
+## sample DHCP ipv6Type ##
+#ipv6Type=DHCP
+## sample STATIC ipv6Type ##
+ipv6Type=STATIC
+ipv6_prefix=128
+ipv6_ip=fc00:10:31:11::34
+
+
+[WINNICS]
+# Valid values: ENABLE, DISABLE, USE_DHCP
+netBiosMode=ENABLE
+dnsServers=10.11.0.1,8.8.8.8
+dnsDomain=testabc.com
+winsServers=192.168.1.113,10.11.0.1
+
+
+[DNS]
+dnsServers=10.1.2.3,8.8.8.8
+dnsSuffixs=test.abc.com,test2.com
diff --git a/setup.py b/setup.py
index 5d6d541d..24f57f67 100644
--- a/setup.py
+++ b/setup.py
@@ -13,9 +13,9 @@ setup(name='vSphere Automation SDK',
install_requires=[
'lxml >= 4.3.0',
'pyVmomi >= 6.7',
- 'vapi-runtime @ file://localhost/{}/lib/vapi-runtime/vapi_runtime-2.25.0-py2.py3-none-any.whl'.format(os.getcwd()),
- 'vapi-client-bindings @ file://localhost/{}/lib/vapi-client-bindings/vapi_client_bindings-3.6.0-py2.py3-none-any.whl'.format(os.getcwd()),
- 'vapi-common-client @ file://localhost/{}/lib/vapi-common-client/vapi_common_client-2.25.0-py2.py3-none-any.whl'.format(os.getcwd()),
+ 'vapi-runtime @ file://localhost/{}/lib/vapi-runtime/vapi_runtime-2.30.0-py2.py3-none-any.whl'.format(os.getcwd()),
+ 'vapi-client-bindings @ file://localhost/{}/lib/vapi-client-bindings/vapi_client_bindings-3.7.0-py2.py3-none-any.whl'.format(os.getcwd()),
+ 'vapi-common-client @ file://localhost/{}/lib/vapi-common-client/vapi_common_client-2.30.0-py2.py3-none-any.whl'.format(os.getcwd()),
'vmc-client-bindings @ file://localhost/{}/lib/vmc-client-bindings/vmc_client_bindings-1.52.0-py2.py3-none-any.whl'.format(os.getcwd()),
'nsx-python-sdk @ file://localhost/{}/lib/nsx-python-sdk/nsx_python_sdk-3.1.2.1.1-py2.py3-none-any.whl'.format(os.getcwd()),
'nsx-policy-python-sdk @ file://localhost/{}/lib/nsx-policy-python-sdk/nsx_policy_python_sdk-3.1.2.1.1-py2.py3-none-any.whl'.format(os.getcwd()),