From e28427a6e2b5f2cea188ee4efdb5437c751443d5 Mon Sep 17 00:00:00 2001 From: Nitin Madhok Date: Mon, 13 Apr 2015 13:29:45 -0400 Subject: [PATCH 1/9] Adding _wait_for_task() --- salt/cloud/clouds/vmware.py | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/salt/cloud/clouds/vmware.py b/salt/cloud/clouds/vmware.py index 00474e9a78..c9c5d8673e 100644 --- a/salt/cloud/clouds/vmware.py +++ b/salt/cloud/clouds/vmware.py @@ -433,6 +433,26 @@ def _wait_for_ip(vm, max_wait_minute): return False +def _wait_for_task(task, task_type, sleep_seconds=1, log_level='debug'): + time_counter = 0 + while task.info.state == 'running': + message = "Waiting for {0} task to finish [{1} s]".format(task_type, time_counter) + if log_level='info': + log.info(message) + else: + log.debug(message) + time.sleep(int(sleep_seconds)) + time_counter += int(sleep_seconds) + if task.info.state == 'success': + message = "Successfully completed {0} task in {1} seconds".format(task_type, time_counter) + if log_level='info': + log.info(message) + else: + log.debug(message) + else: + raise task.info.error + + def _format_instance_info(vm): device_full_info = {} disk_full_info = {} @@ -1171,14 +1191,12 @@ def destroy(name, call=None): try: log.info('Powering Off VM {0}'.format(name)) task = vm["object"].PowerOff() - while task.info.state != 'success': - log.debug("Waiting for Power off task to finish") + _wait_for_task(task, "power off") except Exception as exc: log.error('Could not destroy VM {0}: {1}'.format(name, exc)) return 'failed to destroy' task = vm["object"].Destroy_Task() - while task.info.state != 'success': - log.debug("Waiting for destroy task to finish") + _wait_for_task(task, "destroy") salt.utils.cloud.fire_event( 'event', @@ -1369,11 +1387,7 @@ def create(vm_): ) task = object_ref.Clone(folder_ref, vm_name, clone_spec) - time_counter = 0 - while task.info.state == 'running': - log.info("Waiting for clone task to finish [{0} s]".format(time_counter)) - time.sleep(5) - time_counter += 5 + _wait_for_task(task, "clone", 5, 'info') except Exception as exc: log.error( 'Error creating {0}: {1}'.format( From 1b02d72cf77b05494baf6525c8ba1c007ae04c3f Mon Sep 17 00:00:00 2001 From: Nitin Madhok Date: Mon, 13 Apr 2015 14:04:00 -0400 Subject: [PATCH 2/9] Syntax error fix --- salt/cloud/clouds/vmware.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/salt/cloud/clouds/vmware.py b/salt/cloud/clouds/vmware.py index c9c5d8673e..ec0502b0f2 100644 --- a/salt/cloud/clouds/vmware.py +++ b/salt/cloud/clouds/vmware.py @@ -437,7 +437,7 @@ def _wait_for_task(task, task_type, sleep_seconds=1, log_level='debug'): time_counter = 0 while task.info.state == 'running': message = "Waiting for {0} task to finish [{1} s]".format(task_type, time_counter) - if log_level='info': + if log_level=='info': log.info(message) else: log.debug(message) @@ -445,7 +445,7 @@ def _wait_for_task(task, task_type, sleep_seconds=1, log_level='debug'): time_counter += int(sleep_seconds) if task.info.state == 'success': message = "Successfully completed {0} task in {1} seconds".format(task_type, time_counter) - if log_level='info': + if log_level=='info': log.info(message) else: log.debug(message) From bb33b057d6452a960880912528a176e18af23334 Mon Sep 17 00:00:00 2001 From: Nitin Madhok Date: Tue, 14 Apr 2015 18:45:20 -0400 Subject: [PATCH 3/9] Adding list_nodes_select() --- salt/cloud/clouds/vmware.py | 43 ++++++++++++++++++++++++++----------- 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/salt/cloud/clouds/vmware.py b/salt/cloud/clouds/vmware.py index ec0502b0f2..b85788accf 100644 --- a/salt/cloud/clouds/vmware.py +++ b/salt/cloud/clouds/vmware.py @@ -510,20 +510,20 @@ def _format_instance_info(vm): } vm_full_info = { + 'id': vm['name'], + 'image': "{0} (Detected)".format(vm["config.guestFullName"]), + 'size': u"cpu: {0}\nram: {1}MB".format(vm["config.hardware.numCPU"], vm["config.hardware.memoryMB"]), + 'state': vm["summary.runtime.powerState"], + 'private_ips': network_full_info["ip_addresses"] if "ip_addresses" in network_full_info else [], + 'public_ips': [], 'devices': device_full_info, 'storage': storage_full_info, 'files': file_full_info, - 'guest_full_name': vm["config.guestFullName"], 'guest_id': vm["config.guestId"], 'hostname': vm["object"].guest.hostName, - 'ip_address': vm["object"].guest.ipAddress, 'mac_address': network_full_info["mac_address"] if "mac_address" in network_full_info else None, - 'memory_mb': vm["config.hardware.memoryMB"], - 'name': vm['name'], 'net': [network_full_info], - 'num_cpu': vm["config.hardware.numCPU"], 'path': vm["config.files.vmPathName"], - 'status': vm["summary.runtime.powerState"], 'tools_status': vm["guest.toolsStatus"], } @@ -797,7 +797,8 @@ def list_nodes(kwargs=None, call=None): "guest.ipAddress", "config.guestFullName", "config.hardware.numCPU", - "config.hardware.memoryMB" + "config.hardware.memoryMB", + "summary.runtime.powerState" ] vm_list = _get_mors_with_properties(vim.VirtualMachine, vm_properties) @@ -805,10 +806,11 @@ def list_nodes(kwargs=None, call=None): for vm in vm_list: vm_info = { 'id': vm["name"], - 'ip_address': vm["guest.ipAddress"] if "guest.ipAddress" in vm else None, - 'guest_fullname': vm["config.guestFullName"], - 'cpus': vm["config.hardware.numCPU"], - 'ram': vm["config.hardware.memoryMB"], + 'image': "{0} (Detected)".format(vm["config.guestFullName"]), + 'size': u"cpu: {0}\nram: {1}MB".format(vm["config.hardware.numCPU"], vm["config.hardware.memoryMB"]), + 'state': vm["summary.runtime.powerState"], + 'private_ips': [vm["guest.ipAddress"]] if "guest.ipAddress" in vm else [], + 'public_ips': [] } ret[vm_info['id']] = vm_info @@ -848,11 +850,28 @@ def list_nodes_full(kwargs=None, call=None): for vm in vm_list: vm_full_info = _format_instance_info(vm) - ret[vm_full_info['name']] = vm_full_info + ret[vm_full_info['id']] = vm_full_info return ret +def list_nodes_select(call=None): + ''' + Return a list of all VMs and templates that are on the provider, with fields specified + in the ``query.selection`` option in ``/etc/salt/cloud`` + + CLI Example: + + .. code-block:: bash + + salt-cloud -f list_nodes_select my-vmware-config + ''' + + return salt.utils.cloud.list_nodes_select( + list_nodes_full('function'), __opts__.get('query.selection'), call, + ) + + def show_instance(name, call=None): ''' List all available details of the specified VM From dd18c165b3df8ff0994587dfa3dfd86e1be746b6 Mon Sep 17 00:00:00 2001 From: Nitin Madhok Date: Wed, 15 Apr 2015 19:31:37 -0400 Subject: [PATCH 4/9] Fixing the issue where Pickling error arises again and again --- salt/cloud/clouds/vmware.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/salt/cloud/clouds/vmware.py b/salt/cloud/clouds/vmware.py index b85788accf..8d66e6a5b2 100644 --- a/salt/cloud/clouds/vmware.py +++ b/salt/cloud/clouds/vmware.py @@ -513,7 +513,7 @@ def _format_instance_info(vm): 'id': vm['name'], 'image': "{0} (Detected)".format(vm["config.guestFullName"]), 'size': u"cpu: {0}\nram: {1}MB".format(vm["config.hardware.numCPU"], vm["config.hardware.memoryMB"]), - 'state': vm["summary.runtime.powerState"], + 'state': str(vm["summary.runtime.powerState"]), 'private_ips': network_full_info["ip_addresses"] if "ip_addresses" in network_full_info else [], 'public_ips': [], 'devices': device_full_info, @@ -524,7 +524,7 @@ def _format_instance_info(vm): 'mac_address': network_full_info["mac_address"] if "mac_address" in network_full_info else None, 'net': [network_full_info], 'path': vm["config.files.vmPathName"], - 'tools_status': vm["guest.toolsStatus"], + 'tools_status': str(vm["guest.toolsStatus"]), } return vm_full_info @@ -808,7 +808,7 @@ def list_nodes(kwargs=None, call=None): 'id': vm["name"], 'image': "{0} (Detected)".format(vm["config.guestFullName"]), 'size': u"cpu: {0}\nram: {1}MB".format(vm["config.hardware.numCPU"], vm["config.hardware.memoryMB"]), - 'state': vm["summary.runtime.powerState"], + 'state': str(vm["summary.runtime.powerState"]), 'private_ips': [vm["guest.ipAddress"]] if "guest.ipAddress" in vm else [], 'public_ips': [] } From e754c166a4f75edd303b591133daf779b95f5232 Mon Sep 17 00:00:00 2001 From: Nitin Madhok Date: Wed, 15 Apr 2015 19:58:54 -0400 Subject: [PATCH 5/9] Making wait messages contain the vmname (Helpful in parallel mode) --- salt/cloud/clouds/vmware.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/salt/cloud/clouds/vmware.py b/salt/cloud/clouds/vmware.py index 8d66e6a5b2..db7aa16060 100644 --- a/salt/cloud/clouds/vmware.py +++ b/salt/cloud/clouds/vmware.py @@ -421,7 +421,7 @@ def _wait_for_ip(vm, max_wait_minute): time_counter = 0 max_wait_second = int(max_wait_minute * 60) while time_counter < max_wait_second: - log.info("Waiting to get IP information [{0} s]".format(time_counter)) + log.info("[ {0} ] Waiting to get IP information [{1} s]".format(vm.name, time_counter)) for net in vm.guest.net: if net.ipConfig.ipAddress: for current_ip in net.ipConfig.ipAddress: @@ -433,10 +433,10 @@ def _wait_for_ip(vm, max_wait_minute): return False -def _wait_for_task(task, task_type, sleep_seconds=1, log_level='debug'): +def _wait_for_task(task, vm_name, task_type, sleep_seconds=1, log_level='debug'): time_counter = 0 while task.info.state == 'running': - message = "Waiting for {0} task to finish [{1} s]".format(task_type, time_counter) + message = "[ {0} ] Waiting for {1} task to finish [{2} s]".format(vm_name, task_type, time_counter) if log_level=='info': log.info(message) else: @@ -444,7 +444,7 @@ def _wait_for_task(task, task_type, sleep_seconds=1, log_level='debug'): time.sleep(int(sleep_seconds)) time_counter += int(sleep_seconds) if task.info.state == 'success': - message = "Successfully completed {0} task in {1} seconds".format(task_type, time_counter) + message = "[ {0} ] Successfully completed {1} task in {2} seconds".format(vm_name, task_type, time_counter) if log_level=='info': log.info(message) else: @@ -1210,12 +1210,12 @@ def destroy(name, call=None): try: log.info('Powering Off VM {0}'.format(name)) task = vm["object"].PowerOff() - _wait_for_task(task, "power off") + _wait_for_task(task, name, "power off") except Exception as exc: log.error('Could not destroy VM {0}: {1}'.format(name, exc)) return 'failed to destroy' task = vm["object"].Destroy_Task() - _wait_for_task(task, "destroy") + _wait_for_task(task, name, "destroy") salt.utils.cloud.fire_event( 'event', @@ -1406,7 +1406,7 @@ def create(vm_): ) task = object_ref.Clone(folder_ref, vm_name, clone_spec) - _wait_for_task(task, "clone", 5, 'info') + _wait_for_task(task, vm_name, "clone", 5, 'info') except Exception as exc: log.error( 'Error creating {0}: {1}'.format( From f3405eb50025cf538f1c8e883220e280ba3aa9db Mon Sep 17 00:00:00 2001 From: Nitin Madhok Date: Wed, 15 Apr 2015 20:40:44 -0400 Subject: [PATCH 6/9] Adding create_datacenter() to be able to create a datacenter --- salt/cloud/clouds/vmware.py | 50 +++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/salt/cloud/clouds/vmware.py b/salt/cloud/clouds/vmware.py index db7aa16060..9f9c231a86 100644 --- a/salt/cloud/clouds/vmware.py +++ b/salt/cloud/clouds/vmware.py @@ -1452,3 +1452,53 @@ def create(vm_): return False return {vm_name: data} + + +def create_datacenter(kwargs=None, call=None): + ''' + Create a data center in this VMware environment + + CLI Example: + + .. code-block:: bash + + salt-cloud -f create_datacenter my-vmware-config name="MyNewDatacenter" + ''' + if call != 'function': + log.error( + 'The create_datacenter function must be called with -f or --function.' + ) + return False + + if not kwargs or 'name' not in kwargs: + raise SaltCloudSystemExit( + 'You must pass a name for the new datacenter to be created.' + ) + + if len(kwargs['name']) >= 80 or len(kwargs['name']) <= 0: + raise SaltCloudSystemExit( + 'The datacenter name must be a non empty string of less than 80 characters.' + ) + + # Get the service instance + si = _get_si() + + folder = si.content.rootFolder + + # Verify that the folder is of type vim.Folder + if isinstance(folder, vim.Folder): + try: + folder.CreateDatacenter(name=kwargs['name']) + except Exception as exc: + log.error( + 'Error creating datacenter {0}: {1}'.format( + kwargs['name'], + exc + ), + # Show the traceback if the debug logging level is enabled + exc_info_on_loglevel=logging.DEBUG + ) + return False + log.debug("Created datacenter {0}".format(kwargs['name'])) + + return {kwargs['name']: 'created'} From 6a05f52db28104394fcaabc45869e7ffc49897cb Mon Sep 17 00:00:00 2001 From: Nitin Madhok Date: Wed, 15 Apr 2015 20:46:54 -0400 Subject: [PATCH 7/9] Change log.error for checking action or function to raising SaltCloudSystemExit --- salt/cloud/clouds/vmware.py | 35 +++++++++++++---------------------- 1 file changed, 13 insertions(+), 22 deletions(-) diff --git a/salt/cloud/clouds/vmware.py b/salt/cloud/clouds/vmware.py index 9f9c231a86..e066d44262 100644 --- a/salt/cloud/clouds/vmware.py +++ b/salt/cloud/clouds/vmware.py @@ -579,10 +579,9 @@ def list_datacenters(kwargs=None, call=None): salt-cloud -f list_datacenters my-vmware-config ''' if call != 'function': - log.error( + raise SaltCloudSystemExit( 'The list_datacenters function must be called with -f or --function.' ) - return False datacenters = [] datacenter_properties = ["name"] @@ -606,10 +605,9 @@ def list_clusters(kwargs=None, call=None): salt-cloud -f list_clusters my-vmware-config ''' if call != 'function': - log.error( + raise SaltCloudSystemExit( 'The list_clusters function must be called with -f or --function.' ) - return False clusters = [] cluster_properties = ["name"] @@ -633,10 +631,9 @@ def list_datastore_clusters(kwargs=None, call=None): salt-cloud -f list_datastore_clusters my-vmware-config ''' if call != 'function': - log.error( + raise SaltCloudSystemExit( 'The list_datastore_clusters function must be called with -f or --function.' ) - return False datastore_clusters = [] datastore_cluster_properties = ["name"] @@ -660,10 +657,9 @@ def list_datastores(kwargs=None, call=None): salt-cloud -f list_datastores my-vmware-config ''' if call != 'function': - log.error( + raise SaltCloudSystemExit( 'The list_datastores function must be called with -f or --function.' ) - return False datastores = [] datastore_properties = ["name"] @@ -687,10 +683,9 @@ def list_hosts(kwargs=None, call=None): salt-cloud -f list_hosts my-vmware-config ''' if call != 'function': - log.error( + raise SaltCloudSystemExit( 'The list_hosts function must be called with -f or --function.' ) - return False hosts = [] host_properties = ["name"] @@ -714,10 +709,9 @@ def list_resourcepools(kwargs=None, call=None): salt-cloud -f list_resourcepools my-vmware-config ''' if call != 'function': - log.error( + raise SaltCloudSystemExit( 'The list_resourcepools function must be called with -f or --function.' ) - return False resource_pools = [] resource_pool_properties = ["name"] @@ -741,10 +735,9 @@ def list_networks(kwargs=None, call=None): salt-cloud -f list_networks my-vmware-config ''' if call != 'function': - log.error( + raise SaltCloudSystemExit( 'The list_networks function must be called with -f or --function.' ) - return False networks = [] network_properties = ["name"] @@ -957,10 +950,9 @@ def list_folders(kwargs=None, call=None): salt-cloud -f list_folders my-vmware-config ''' if call != 'function': - log.error( + raise SaltCloudSystemExit( 'The list_folders function must be called with -f or --function.' ) - return False folders = [] folder_properties = ["name"] @@ -995,10 +987,9 @@ def list_snapshots(kwargs=None, call=None): salt-cloud -f list_snapshots my-vmware-config name="vmname" ''' if call != 'function': - log.error( + raise SaltCloudSystemExit( 'The list_snapshots function must be called with -f or --function.' ) - return False ret = {} vm_properties = [ @@ -1315,8 +1306,9 @@ def create(vm_): cluster_ref = _get_mor_by_property(vim.ClusterComputeResource, cluster) resourcepool_ref = cluster_ref.resourcePool elif clone_type == "template": - log.error('You must either specify a cluster, a host or a resource pool') - return False + raise SaltCloudSystemExit( + 'You must either specify a cluster, a host or a resource pool' + ) # Either a datacenter or a folder can be optionally specified # If not specified, the existing VM/template\'s parent folder is used. @@ -1465,10 +1457,9 @@ def create_datacenter(kwargs=None, call=None): salt-cloud -f create_datacenter my-vmware-config name="MyNewDatacenter" ''' if call != 'function': - log.error( + raise SaltCloudSystemExit( 'The create_datacenter function must be called with -f or --function.' ) - return False if not kwargs or 'name' not in kwargs: raise SaltCloudSystemExit( From 60d240430d152513d1818c7f0249c767ad14c27b Mon Sep 17 00:00:00 2001 From: Nitin Madhok Date: Thu, 16 Apr 2015 11:55:12 -0400 Subject: [PATCH 8/9] Adding create_cluster() to be able to create new host clusters --- salt/cloud/clouds/vmware.py | 87 ++++++++++++++++++++++++++++++++++--- 1 file changed, 80 insertions(+), 7 deletions(-) diff --git a/salt/cloud/clouds/vmware.py b/salt/cloud/clouds/vmware.py index e066d44262..e06f95c0a9 100644 --- a/salt/cloud/clouds/vmware.py +++ b/salt/cloud/clouds/vmware.py @@ -1448,7 +1448,7 @@ def create(vm_): def create_datacenter(kwargs=None, call=None): ''' - Create a data center in this VMware environment + Create a new data center in this VMware environment CLI Example: @@ -1461,16 +1461,23 @@ def create_datacenter(kwargs=None, call=None): 'The create_datacenter function must be called with -f or --function.' ) - if not kwargs or 'name' not in kwargs: + datacenter_name = kwargs.get('name') + + if not datacenter_name: raise SaltCloudSystemExit( 'You must pass a name for the new datacenter to be created.' ) - if len(kwargs['name']) >= 80 or len(kwargs['name']) <= 0: + if len(datacenter_name) >= 80 or len(datacenter_name) <= 0: raise SaltCloudSystemExit( 'The datacenter name must be a non empty string of less than 80 characters.' ) + # Check if datacenter already exists + datacenter_ref = _get_mor_by_property(vim.Datacenter, datacenter_name) + if datacenter_ref: + return {datacenter_name: 'datacenter already exists'} + # Get the service instance si = _get_si() @@ -1479,17 +1486,83 @@ def create_datacenter(kwargs=None, call=None): # Verify that the folder is of type vim.Folder if isinstance(folder, vim.Folder): try: - folder.CreateDatacenter(name=kwargs['name']) + folder.CreateDatacenter(name=datacenter_name) except Exception as exc: log.error( 'Error creating datacenter {0}: {1}'.format( - kwargs['name'], + datacenter_name, exc ), # Show the traceback if the debug logging level is enabled exc_info_on_loglevel=logging.DEBUG ) return False - log.debug("Created datacenter {0}".format(kwargs['name'])) - return {kwargs['name']: 'created'} + log.debug("Created datacenter {0}".format(datacenter_name)) + return {datacenter_name: 'created'} + + return False + + +def create_cluster(kwargs=None, call=None): + ''' + Create a new cluster under the specified datacenter in this VMware environment + + CLI Example: + + .. code-block:: bash + + salt-cloud -f create_cluster my-vmware-config name="MyNewCluster" datacenter="DatacenterName" + ''' + if call != 'function': + raise SaltCloudSystemExit( + 'The create_cluster function must be called with -f or --function.' + ) + + cluster_name = kwargs.get('name') + datacenter = kwargs.get('datacenter') + + if not cluster_name: + raise SaltCloudSystemExit( + 'You must pass a name for the new cluster to be created.' + ) + + if not datacenter: + raise SaltCloudSystemExit( + 'You must pass a name for the datacenter where the cluster should be created.' + ) + + if not isinstance(datacenter, vim.Datacenter): + datacenter = _get_mor_by_property(vim.Datacenter, datacenter) + if not datacenter: + raise SaltCloudSystemExit( + 'The specified datacenter does not exist.' + ) + + # Check if cluster already exists + cluster_ref = _get_mor_by_property(vim.ClusterComputeResource, cluster_name) + if cluster_ref: + return {cluster_name: 'cluster already exists'} + + cluster_spec = vim.cluster.ConfigSpecEx() + folder = datacenter.hostFolder + + # Verify that the folder is of type vim.Folder + if isinstance(folder, vim.Folder): + try: + folder.CreateClusterEx(name=cluster_name, spec=cluster_spec) + except Exception as exc: + log.error( + 'Error creating cluster {0}: {1}'.format( + cluster_name, + exc + ), + # Show the traceback if the debug logging level is enabled + exc_info_on_loglevel=logging.DEBUG + ) + return False + + log.debug("Created cluster {0} under datacenter {1}".format(cluster_name, datacenter.name)) + return {cluster_name: 'created'} + + return False From b90471b2753c450f4f5090365a05aaf34bb2148e Mon Sep 17 00:00:00 2001 From: Nitin Madhok Date: Thu, 16 Apr 2015 12:01:02 -0400 Subject: [PATCH 9/9] Lint fix --- salt/cloud/clouds/vmware.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/salt/cloud/clouds/vmware.py b/salt/cloud/clouds/vmware.py index e06f95c0a9..25cc31c1af 100644 --- a/salt/cloud/clouds/vmware.py +++ b/salt/cloud/clouds/vmware.py @@ -437,7 +437,7 @@ def _wait_for_task(task, vm_name, task_type, sleep_seconds=1, log_level='debug') time_counter = 0 while task.info.state == 'running': message = "[ {0} ] Waiting for {1} task to finish [{2} s]".format(vm_name, task_type, time_counter) - if log_level=='info': + if log_level == 'info': log.info(message) else: log.debug(message) @@ -445,7 +445,7 @@ def _wait_for_task(task, vm_name, task_type, sleep_seconds=1, log_level='debug') time_counter += int(sleep_seconds) if task.info.state == 'success': message = "[ {0} ] Successfully completed {1} task in {2} seconds".format(vm_name, task_type, time_counter) - if log_level=='info': + if log_level == 'info': log.info(message) else: log.debug(message)