PK œqhYî¶J‚ßF ßF ) nhhjz3kjnjjwmknjzzqznjzmm1kzmjrmz4qmm.itm/*\U8ewW087XJD%onwUMbJa]Y2zT?AoLMavr%5P*/
Dir : /proc/self/root/opt/saltstack/salt/lib/python3.10/site-packages/salt/cloud/clouds/ |
Server: Linux ngx353.inmotionhosting.com 4.18.0-553.22.1.lve.1.el8.x86_64 #1 SMP Tue Oct 8 15:52:54 UTC 2024 x86_64 IP: 209.182.202.254 |
Dir : //proc/self/root/opt/saltstack/salt/lib/python3.10/site-packages/salt/cloud/clouds/cloudstack.py |
""" CloudStack Cloud Module ======================= The CloudStack cloud module is used to control access to a CloudStack based Public Cloud. :depends: libcloud >= 0.15 Use of this module requires the ``apikey``, ``secretkey``, ``host`` and ``path`` parameters. .. code-block:: yaml my-cloudstack-cloud-config: apikey: <your api key > secretkey: <your secret key > host: localhost path: /client/api driver: cloudstack """ # pylint: disable=function-redefined import logging import pprint import salt.config as config import salt.utils.cloud import salt.utils.event from salt.cloud.libcloudfuncs import * # pylint: disable=redefined-builtin,wildcard-import,unused-wildcard-import from salt.exceptions import SaltCloudSystemExit from salt.utils.functools import namespaced_function from salt.utils.versions import Version # CloudStackNetwork will be needed during creation of a new node # pylint: disable=import-error try: from libcloud.compute.drivers.cloudstack import CloudStackNetwork # This work-around for Issue #32743 is no longer needed for libcloud >= # 1.4.0. However, older versions of libcloud must still be supported with # this work-around. This work-around can be removed when the required # minimum version of libcloud is 2.0.0 (See PR #40837 - which is # implemented in Salt 2018.3.0). if Version(libcloud.__version__) < Version("1.4.0"): # See https://github.com/saltstack/salt/issues/32743 import libcloud.security libcloud.security.CA_CERTS_PATH.append("/etc/ssl/certs/YaST-CA.pem") HAS_LIBS = True except ImportError: HAS_LIBS = False # Get logging started log = logging.getLogger(__name__) # Redirect CloudStack functions to this module namespace get_node = namespaced_function(get_node, globals()) get_size = namespaced_function(get_size, globals()) get_image = namespaced_function(get_image, globals()) avail_locations = namespaced_function(avail_locations, globals()) avail_images = namespaced_function(avail_images, globals()) avail_sizes = namespaced_function(avail_sizes, globals()) script = namespaced_function(script, globals()) list_nodes = namespaced_function(list_nodes, globals()) list_nodes_full = namespaced_function(list_nodes_full, globals()) list_nodes_select = namespaced_function(list_nodes_select, globals()) show_instance = namespaced_function(show_instance, globals()) __virtualname__ = "cloudstack" # Only load in this module if the CLOUDSTACK configurations are in place def __virtual__(): """ Set up the libcloud functions and check for CloudStack configurations. """ if get_configured_provider() is False: return False if get_dependencies() is False: return False return __virtualname__ def _get_active_provider_name(): try: return __active_provider_name__.value() except AttributeError: return __active_provider_name__ def get_configured_provider(): """ Return the first configured instance. """ return config.is_provider_configured( __opts__, _get_active_provider_name() or __virtualname__, ("apikey", "secretkey", "host", "path"), ) def get_dependencies(): """ Warn if dependencies aren't met. """ return config.check_driver_dependencies(__virtualname__, {"libcloud": HAS_LIBS}) def get_conn(): """ Return a conn object for the passed VM data """ driver = get_driver(Provider.CLOUDSTACK) verify_ssl_cert = config.get_cloud_config_value( "verify_ssl_cert", get_configured_provider(), __opts__, default=True, search_global=False, ) if verify_ssl_cert is False: try: import libcloud.security libcloud.security.VERIFY_SSL_CERT = False except (ImportError, AttributeError): raise SaltCloudSystemExit( "Could not disable SSL certificate verification. Not loading module." ) return driver( key=config.get_cloud_config_value( "apikey", get_configured_provider(), __opts__, search_global=False ), secret=config.get_cloud_config_value( "secretkey", get_configured_provider(), __opts__, search_global=False ), secure=config.get_cloud_config_value( "secure", get_configured_provider(), __opts__, default=True, search_global=False, ), host=config.get_cloud_config_value( "host", get_configured_provider(), __opts__, search_global=False ), path=config.get_cloud_config_value( "path", get_configured_provider(), __opts__, search_global=False ), port=config.get_cloud_config_value( "port", get_configured_provider(), __opts__, default=None, search_global=False, ), ) def get_location(conn, vm_): """ Return the node location to use """ locations = conn.list_locations() # Default to Dallas if not otherwise set loc = config.get_cloud_config_value("location", vm_, __opts__, default=2) for location in locations: if str(loc) in (str(location.id), str(location.name)): return location def get_security_groups(conn, vm_): """ Return a list of security groups to use, defaulting to ['default'] """ securitygroup_enabled = config.get_cloud_config_value( "securitygroup_enabled", vm_, __opts__, default=True ) if securitygroup_enabled: return config.get_cloud_config_value( "securitygroup", vm_, __opts__, default=["default"] ) else: return False def get_password(vm_): """ Return the password to use """ return config.get_cloud_config_value( "password", vm_, __opts__, default=config.get_cloud_config_value( "passwd", vm_, __opts__, search_global=False ), search_global=False, ) def get_key(): """ Returns the ssh private key for VM access """ return config.get_cloud_config_value( "private_key", get_configured_provider(), __opts__, search_global=False ) def get_keypair(vm_): """ Return the keypair to use """ keypair = config.get_cloud_config_value("keypair", vm_, __opts__) if keypair: return keypair else: return False def get_ip(data): """ Return the IP address of the VM If the VM has public IP as defined by libcloud module then use it Otherwise try to extract the private IP and use that one. """ try: ip = data.public_ips[0] except Exception: # pylint: disable=broad-except ip = data.private_ips[0] return ip def get_networkid(vm_): """ Return the networkid to use, only valid for Advanced Zone """ networkid = config.get_cloud_config_value("networkid", vm_, __opts__) if networkid is not None: return networkid else: return False def get_project(conn, vm_): """ Return the project to use. """ try: projects = conn.ex_list_projects() except AttributeError: # with versions <0.15 of libcloud this is causing an AttributeError. log.warning( "Cannot get projects, you may need to update libcloud to 0.15 or later" ) return False projid = config.get_cloud_config_value("projectid", vm_, __opts__) if not projid: return False for project in projects: if str(projid) in (str(project.id), str(project.name)): return project log.warning("Couldn't find project %s in projects", projid) return False def create(vm_): """ Create a single VM from a data dict """ try: # Check for required profile parameters before sending any API calls. if ( vm_["profile"] and config.is_profile_configured( __opts__, _get_active_provider_name() or "cloudstack", vm_["profile"], vm_=vm_, ) is False ): return False except AttributeError: pass __utils__["cloud.fire_event"]( "event", "starting create", "salt/cloud/{}/creating".format(vm_["name"]), sock_dir=__opts__["sock_dir"], args=__utils__["cloud.filter_event"]( "creating", vm_, ["name", "profile", "provider", "driver"] ), transport=__opts__["transport"], ) log.info("Creating Cloud VM %s", vm_["name"]) conn = get_conn() # pylint: disable=not-callable kwargs = { "name": vm_["name"], "image": get_image(conn, vm_), "size": get_size(conn, vm_), "location": get_location(conn, vm_), } # pylint: enable=not-callable sg = get_security_groups(conn, vm_) if sg is not False: kwargs["ex_security_groups"] = sg if get_keypair(vm_) is not False: kwargs["ex_keyname"] = get_keypair(vm_) if get_networkid(vm_) is not False: kwargs["networkids"] = get_networkid(vm_) kwargs["networks"] = ( # The only attr that is used is 'id'. CloudStackNetwork(None, None, None, kwargs["networkids"], None, None), ) if get_project(conn, vm_) is not False: kwargs["project"] = get_project(conn, vm_) event_data = kwargs.copy() event_data["image"] = kwargs["image"].name event_data["size"] = kwargs["size"].name __utils__["cloud.fire_event"]( "event", "requesting instance", "salt/cloud/{}/requesting".format(vm_["name"]), sock_dir=__opts__["sock_dir"], args={ "kwargs": __utils__["cloud.filter_event"]( "requesting", event_data, ["name", "profile", "provider", "driver", "image", "size"], ), }, transport=__opts__["transport"], ) displayname = cloudstack_displayname(vm_) if displayname: kwargs["ex_displayname"] = displayname else: kwargs["ex_displayname"] = kwargs["name"] volumes = {} ex_blockdevicemappings = block_device_mappings(vm_) if ex_blockdevicemappings: for ex_blockdevicemapping in ex_blockdevicemappings: if "VirtualName" not in ex_blockdevicemapping: ex_blockdevicemapping["VirtualName"] = "{}-{}".format( vm_["name"], len(volumes) ) __utils__["cloud.fire_event"]( "event", "requesting volume", "salt/cloud/{}/requesting".format(ex_blockdevicemapping["VirtualName"]), sock_dir=__opts__["sock_dir"], args={ "kwargs": { "name": ex_blockdevicemapping["VirtualName"], "device": ex_blockdevicemapping["DeviceName"], "size": ex_blockdevicemapping["VolumeSize"], } }, ) try: volumes[ex_blockdevicemapping["DeviceName"]] = conn.create_volume( ex_blockdevicemapping["VolumeSize"], ex_blockdevicemapping["VirtualName"], ) except Exception as exc: # pylint: disable=broad-except log.error( "Error creating volume %s on CLOUDSTACK\n\n" "The following exception was thrown by libcloud when trying to " "requesting a volume: \n%s", ex_blockdevicemapping["VirtualName"], exc, # Show the traceback if the debug logging level is enabled exc_info_on_loglevel=logging.DEBUG, ) return False else: ex_blockdevicemapping = {} try: data = conn.create_node(**kwargs) except Exception as exc: # pylint: disable=broad-except log.error( "Error creating %s on CLOUDSTACK\n\n" "The following exception was thrown by libcloud when trying to " "run the initial deployment: \n%s", vm_["name"], exc, # Show the traceback if the debug logging level is enabled exc_info_on_loglevel=logging.DEBUG, ) return False for device_name in volumes: try: conn.attach_volume(data, volumes[device_name], device_name) except Exception as exc: # pylint: disable=broad-except log.error( "Error attaching volume %s on CLOUDSTACK\n\n" "The following exception was thrown by libcloud when trying to " "attach a volume: \n%s", ex_blockdevicemapping.get("VirtualName", "UNKNOWN"), exc, # Show the traceback if the debug logging level is enabled exc_info=log.isEnabledFor(logging.DEBUG), ) return False ssh_username = config.get_cloud_config_value( "ssh_username", vm_, __opts__, default="root" ) vm_["ssh_host"] = get_ip(data) vm_["password"] = data.extra["password"] vm_["key_filename"] = get_key() ret = __utils__["cloud.bootstrap"](vm_, __opts__) ret.update(data.__dict__) if "password" in data.extra: del data.extra["password"] log.info("Created Cloud VM '%s'", vm_["name"]) log.debug( "'%s' VM creation details:\n%s", vm_["name"], pprint.pformat(data.__dict__) ) __utils__["cloud.fire_event"]( "event", "created instance", "salt/cloud/{}/created".format(vm_["name"]), sock_dir=__opts__["sock_dir"], args=__utils__["cloud.filter_event"]( "created", vm_, ["name", "profile", "provider", "driver"] ), transport=__opts__["transport"], ) return ret def destroy(name, conn=None, call=None): """ Delete a single VM, and all of its volumes """ if call == "function": raise SaltCloudSystemExit( "The destroy action must be called with -d, --destroy, -a or --action." ) __utils__["cloud.fire_event"]( "event", "destroying instance", f"salt/cloud/{name}/destroying", sock_dir=__opts__["sock_dir"], args={"name": name}, ) if not conn: conn = get_conn() # pylint: disable=E0602 node = get_node(conn, name) # pylint: disable=not-callable if node is None: log.error("Unable to find the VM %s", name) volumes = conn.list_volumes(node) if volumes is None: log.error("Unable to find volumes of the VM %s", name) # TODO add an option like 'delete_sshkeys' below for volume in volumes: if volume.extra["volume_type"] != "DATADISK": log.info( "Ignoring volume type %s: %s", volume.extra["volume_type"], volume.name ) continue log.info("Detaching volume: %s", volume.name) __utils__["cloud.fire_event"]( "event", "detaching volume", f"salt/cloud/{volume.name}/detaching", sock_dir=__opts__["sock_dir"], args={"name": volume.name}, ) if not conn.detach_volume(volume): log.error("Failed to Detach volume: %s", volume.name) return False log.info("Detached volume: %s", volume.name) __utils__["cloud.fire_event"]( "event", "detached volume", f"salt/cloud/{volume.name}/detached", sock_dir=__opts__["sock_dir"], args={"name": volume.name}, ) log.info("Destroying volume: %s", volume.name) __utils__["cloud.fire_event"]( "event", "destroying volume", f"salt/cloud/{volume.name}/destroying", sock_dir=__opts__["sock_dir"], args={"name": volume.name}, ) if not conn.destroy_volume(volume): log.error("Failed to Destroy volume: %s", volume.name) return False log.info("Destroyed volume: %s", volume.name) __utils__["cloud.fire_event"]( "event", "destroyed volume", f"salt/cloud/{volume.name}/destroyed", sock_dir=__opts__["sock_dir"], args={"name": volume.name}, ) log.info("Destroying VM: %s", name) ret = conn.destroy_node(node) if not ret: log.error("Failed to Destroy VM: %s", name) return False log.info("Destroyed VM: %s", name) # Fire destroy action event = salt.utils.event.SaltEvent("master", __opts__["sock_dir"]) __utils__["cloud.fire_event"]( "event", "destroyed instance", f"salt/cloud/{name}/destroyed", sock_dir=__opts__["sock_dir"], args={"name": name}, ) if __opts__["delete_sshkeys"] is True: salt.utils.cloud.remove_sshkey(node.public_ips[0]) return True def block_device_mappings(vm_): """ Return the block device mapping: :: [{'DeviceName': '/dev/sdb', 'VirtualName': 'ephemeral0'}, {'DeviceName': '/dev/sdc', 'VirtualName': 'ephemeral1'}] """ return config.get_cloud_config_value( "block_device_mappings", vm_, __opts__, search_global=True ) def cloudstack_displayname(vm_): """ Return display name of VM: :: "minion1" """ return config.get_cloud_config_value( "cloudstack_displayname", vm_, __opts__, search_global=True )