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/utils/openstack/ |
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/utils/openstack/nova.py |
""" Nova class """ import inspect import logging import time import salt.utils.cloud import salt.utils.files from salt.exceptions import SaltCloudSystemExit from salt.utils.versions import Version HAS_NOVA = False # pylint: disable=import-error try: import novaclient import novaclient.auth_plugin import novaclient.base import novaclient.exceptions import novaclient.extension import novaclient.utils from novaclient import client from novaclient.shell import OpenStackComputeShell HAS_NOVA = True except ImportError: pass HAS_KEYSTONEAUTH = False try: import keystoneauth1.loading import keystoneauth1.session HAS_KEYSTONEAUTH = True except ImportError: pass # pylint: enable=import-error # Get logging started log = logging.getLogger(__name__) # Version added to novaclient.client.Client function NOVACLIENT_MINVER = "2.6.1" NOVACLIENT_MAXVER = "6.0.1" # dict for block_device_mapping_v2 CLIENT_BDM2_KEYS = { "id": "uuid", "source": "source_type", "dest": "destination_type", "bus": "disk_bus", "device": "device_name", "size": "volume_size", "format": "guest_format", "bootindex": "boot_index", "type": "device_type", "shutdown": "delete_on_termination", } def check_nova(): if HAS_NOVA: novaclient_ver = Version(novaclient.__version__) min_ver = Version(NOVACLIENT_MINVER) max_ver = Version(NOVACLIENT_MAXVER) if min_ver <= novaclient_ver <= max_ver: return HAS_NOVA elif novaclient_ver > max_ver: log.debug( "Older novaclient version required. Maximum: %s", NOVACLIENT_MAXVER ) return False log.debug("Newer novaclient version required. Minimum: %s", NOVACLIENT_MINVER) return False if check_nova(): try: import novaclient.auth_plugin except ImportError: log.debug( "Using novaclient version 7.0.0 or newer. Authentication " "plugin auth_plugin.py is not available anymore." ) # kwargs has to be an object instead of a dictionary for the __post_parse_arg__ class KwargsStruct: def __init__(self, **entries): self.__dict__.update(entries) def _parse_block_device_mapping_v2( block_device=None, boot_volume=None, snapshot=None, ephemeral=None, swap=None ): bdm = [] if block_device is None: block_device = [] if ephemeral is None: ephemeral = [] if boot_volume is not None: bdm_dict = { "uuid": boot_volume, "source_type": "volume", "destination_type": "volume", "boot_index": 0, "delete_on_termination": False, } bdm.append(bdm_dict) if snapshot is not None: bdm_dict = { "uuid": snapshot, "source_type": "snapshot", "destination_type": "volume", "boot_index": 0, "delete_on_termination": False, } bdm.append(bdm_dict) for device_spec in block_device: bdm_dict = {} for key, value in device_spec.items(): bdm_dict[CLIENT_BDM2_KEYS[key]] = value # Convert the delete_on_termination to a boolean or set it to true by # default for local block devices when not specified. if "delete_on_termination" in bdm_dict: action = bdm_dict["delete_on_termination"] bdm_dict["delete_on_termination"] = action == "remove" elif bdm_dict.get("destination_type") == "local": bdm_dict["delete_on_termination"] = True bdm.append(bdm_dict) for ephemeral_spec in ephemeral: bdm_dict = { "source_type": "blank", "destination_type": "local", "boot_index": -1, "delete_on_termination": True, } if "size" in ephemeral_spec: bdm_dict["volume_size"] = ephemeral_spec["size"] if "format" in ephemeral_spec: bdm_dict["guest_format"] = ephemeral_spec["format"] bdm.append(bdm_dict) if swap is not None: bdm_dict = { "source_type": "blank", "destination_type": "local", "boot_index": -1, "delete_on_termination": True, "guest_format": "swap", "volume_size": swap, } bdm.append(bdm_dict) return bdm class NovaServer: def __init__(self, name, server, password=None): """ Make output look like libcloud output for consistency """ self.name = name self.id = server["id"] self.image = server.get("image", {}).get("id", "Boot From Volume") self.size = server["flavor"]["id"] self.state = server["state"] self._uuid = None self.extra = {"metadata": server["metadata"], "access_ip": server["accessIPv4"]} self.addresses = server.get("addresses", {}) self.public_ips, self.private_ips = [], [] self.fixed_ips, self.floating_ips = [], [] for network in self.addresses.values(): for addr in network: if salt.utils.cloud.is_public_ip(addr["addr"]): self.public_ips.append(addr["addr"]) else: self.private_ips.append(addr["addr"]) if addr.get("OS-EXT-IPS:type") == "floating": self.floating_ips.append(addr["addr"]) else: self.fixed_ips.append(addr["addr"]) if password: self.extra["password"] = password def __str__(self): return str(self.__dict__) def get_entry(dict_, key, value, raise_error=True): for entry in dict_: if entry[key] == value: return entry if raise_error is True: raise SaltCloudSystemExit(f"Unable to find {key} in {dict_}.") return {} def get_entry_multi(dict_, pairs, raise_error=True): for entry in dict_: if all([entry[key] == value for key, value in pairs]): return entry if raise_error is True: raise SaltCloudSystemExit(f"Unable to find {pairs} in {dict_}.") return {} def get_endpoint_url_v3(catalog, service_type, region_name): for service_entry in catalog: if service_entry["type"] == service_type: for endpoint_entry in service_entry["endpoints"]: if ( endpoint_entry["region"] == region_name and endpoint_entry["interface"] == "public" ): return endpoint_entry["url"] return None def sanatize_novaclient(kwargs): variables = ( "username", "api_key", "project_id", "auth_url", "insecure", "timeout", "proxy_tenant_id", "proxy_token", "region_name", "endpoint_type", "extensions", "service_type", "service_name", "volume_service_name", "timings", "bypass_url", "os_cache", "no_cache", "http_log_debug", "auth_system", "auth_plugin", "auth_token", "cacert", "tenant_id", ) ret = {} for var in kwargs: if var in variables: ret[var] = kwargs[var] return ret # Function alias to not shadow built-ins class SaltNova: """ Class for all novaclient functions """ extensions = [] def __init__( self, username, project_id, auth_url, region_name=None, password=None, os_auth_plugin=None, use_keystoneauth=False, **kwargs, ): """ Set up nova credentials """ if all([use_keystoneauth, HAS_KEYSTONEAUTH]): self._new_init( username=username, project_id=project_id, auth_url=auth_url, region_name=region_name, password=password, os_auth_plugin=os_auth_plugin, **kwargs, ) else: self._old_init( username=username, project_id=project_id, auth_url=auth_url, region_name=region_name, password=password, os_auth_plugin=os_auth_plugin, **kwargs, ) def _new_init( self, username, project_id, auth_url, region_name, password, os_auth_plugin, auth=None, verify=True, **kwargs, ): if auth is None: auth = {} loader = keystoneauth1.loading.get_plugin_loader(os_auth_plugin or "password") self.client_kwargs = kwargs.copy() self.kwargs = auth.copy() if not self.extensions: if hasattr(OpenStackComputeShell, "_discover_extensions"): self.extensions = OpenStackComputeShell()._discover_extensions("2.0") else: self.extensions = client.discover_extensions("2.0") for extension in self.extensions: extension.run_hooks("__pre_parse_args__") self.client_kwargs["extensions"] = self.extensions self.kwargs["username"] = username self.kwargs["project_name"] = project_id self.kwargs["auth_url"] = auth_url self.kwargs["password"] = password if auth_url.endswith("3"): self.kwargs["user_domain_name"] = kwargs.get("user_domain_name", "default") self.kwargs["project_domain_name"] = kwargs.get( "project_domain_name", "default" ) self.client_kwargs["region_name"] = region_name self.client_kwargs["service_type"] = "compute" if hasattr(self, "extensions"): # needs an object, not a dictionary self.kwargstruct = KwargsStruct(**self.client_kwargs) for extension in self.extensions: extension.run_hooks("__post_parse_args__", self.kwargstruct) self.client_kwargs = self.kwargstruct.__dict__ # Requires novaclient version >= 2.6.1 self.version = str(kwargs.get("version", 2)) self.client_kwargs = sanatize_novaclient(self.client_kwargs) options = loader.load_from_options(**self.kwargs) self.session = keystoneauth1.session.Session(auth=options, verify=verify) conn = client.Client( version=self.version, session=self.session, **self.client_kwargs ) self.kwargs["auth_token"] = conn.client.session.get_token() identity_service_type = kwargs.get("identity_service_type", "identity") self.catalog = ( conn.client.session.get( "/auth/catalog", endpoint_filter={"service_type": identity_service_type} ) .json() .get("catalog", []) ) if conn.client.get_endpoint(service_type=identity_service_type).endswith("v3"): self._v3_setup(region_name) else: self._v2_setup(region_name) def _old_init( self, username, project_id, auth_url, region_name, password, os_auth_plugin, **kwargs, ): self.kwargs = kwargs.copy() if not self.extensions: if hasattr(OpenStackComputeShell, "_discover_extensions"): self.extensions = OpenStackComputeShell()._discover_extensions("2.0") else: self.extensions = client.discover_extensions("2.0") for extension in self.extensions: extension.run_hooks("__pre_parse_args__") self.kwargs["extensions"] = self.extensions self.kwargs["username"] = username self.kwargs["project_id"] = project_id self.kwargs["auth_url"] = auth_url self.kwargs["region_name"] = region_name self.kwargs["service_type"] = "compute" # used in novaclient extensions to see if they are rackspace or not, to know if it needs to load # the hooks for that extension or not. This is cleaned up by sanatize_novaclient self.kwargs["os_auth_url"] = auth_url if os_auth_plugin is not None: novaclient.auth_plugin.discover_auth_systems() auth_plugin = novaclient.auth_plugin.load_plugin(os_auth_plugin) self.kwargs["auth_plugin"] = auth_plugin self.kwargs["auth_system"] = os_auth_plugin if not self.kwargs.get("api_key", None): self.kwargs["api_key"] = password # This has to be run before sanatize_novaclient before extra variables are cleaned out. if hasattr(self, "extensions"): # needs an object, not a dictionary self.kwargstruct = KwargsStruct(**self.kwargs) for extension in self.extensions: extension.run_hooks("__post_parse_args__", self.kwargstruct) self.kwargs = self.kwargstruct.__dict__ self.kwargs = sanatize_novaclient(self.kwargs) # Requires novaclient version >= 2.6.1 self.kwargs["version"] = str(kwargs.get("version", 2)) conn = client.Client(**self.kwargs) try: conn.client.authenticate() except novaclient.exceptions.AmbiguousEndpoints: raise SaltCloudSystemExit( "Nova provider requires a 'region_name' to be specified" ) self.kwargs["auth_token"] = conn.client.auth_token self.catalog = conn.client.service_catalog.catalog["access"]["serviceCatalog"] self._v2_setup(region_name) def _v3_setup(self, region_name): if region_name is not None: self.client_kwargs["bypass_url"] = get_endpoint_url_v3( self.catalog, "compute", region_name ) log.debug("Using Nova bypass_url: %s", self.client_kwargs["bypass_url"]) self.compute_conn = client.Client( version=self.version, session=self.session, **self.client_kwargs ) volume_endpoints = get_entry( self.catalog, "type", "volume", raise_error=False ).get("endpoints", {}) if volume_endpoints: if region_name is not None: self.client_kwargs["bypass_url"] = get_endpoint_url_v3( self.catalog, "volume", region_name ) log.debug( "Using Cinder bypass_url: %s", self.client_kwargs["bypass_url"] ) self.volume_conn = client.Client( version=self.version, session=self.session, **self.client_kwargs ) if hasattr(self, "extensions"): self.expand_extensions() else: self.volume_conn = None def _v2_setup(self, region_name): if region_name is not None: servers_endpoints = get_entry(self.catalog, "type", "compute")["endpoints"] self.kwargs["bypass_url"] = get_entry( servers_endpoints, "region", region_name )["publicURL"] self.compute_conn = client.Client(**self.kwargs) volume_endpoints = get_entry( self.catalog, "type", "volume", raise_error=False ).get("endpoints", {}) if volume_endpoints: if region_name is not None: self.kwargs["bypass_url"] = get_entry( volume_endpoints, "region", region_name )["publicURL"] self.volume_conn = client.Client(**self.kwargs) if hasattr(self, "extensions"): self.expand_extensions() else: self.volume_conn = None def expand_extensions(self): for connection in (self.compute_conn, self.volume_conn): if connection is None: continue for extension in self.extensions: for attr in extension.module.__dict__: if not inspect.isclass(getattr(extension.module, attr)): continue for key, value in connection.__dict__.items(): if not isinstance(value, novaclient.base.Manager): continue if value.__class__.__name__ == attr: setattr( connection, key, extension.manager_class(connection) ) def get_catalog(self): """ Return service catalog """ return self.catalog def server_show_libcloud(self, uuid): """ Make output look like libcloud output for consistency """ server_info = self.server_show(uuid) server = next(iter(server_info.values())) server_name = next(iter(server_info.keys())) if not hasattr(self, "password"): self.password = None ret = NovaServer(server_name, server, self.password) return ret def boot(self, name, flavor_id=0, image_id=0, timeout=300, **kwargs): """ Boot a cloud server. """ nt_ks = self.compute_conn kwargs["name"] = name kwargs["flavor"] = flavor_id kwargs["image"] = image_id or None ephemeral = kwargs.pop("ephemeral", []) block_device = kwargs.pop("block_device", []) boot_volume = kwargs.pop("boot_volume", None) snapshot = kwargs.pop("snapshot", None) swap = kwargs.pop("swap", None) kwargs["block_device_mapping_v2"] = _parse_block_device_mapping_v2( block_device=block_device, boot_volume=boot_volume, snapshot=snapshot, ephemeral=ephemeral, swap=swap, ) response = nt_ks.servers.create(**kwargs) self.uuid = response.id self.password = getattr(response, "adminPass", None) start = time.time() trycount = 0 while True: trycount += 1 try: return self.server_show_libcloud(self.uuid) except Exception as exc: # pylint: disable=broad-except log.debug("Server information not yet available: %s", exc) time.sleep(1) if time.time() - start > timeout: log.error( "Timed out after %s seconds while waiting for data", timeout ) return False log.debug("Retrying server_show() (try %s)", trycount) def show_instance(self, name): """ Find a server by its name (libcloud) """ return self.server_by_name(name) def root_password(self, server_id, password): """ Change server(uuid's) root password """ nt_ks = self.compute_conn nt_ks.servers.change_password(server_id, password) def server_by_name(self, name): """ Find a server by its name """ return self.server_show_libcloud(self.server_list().get(name, {}).get("id", "")) def _volume_get(self, volume_id): """ Organize information about a volume from the volume_id """ if self.volume_conn is None: raise SaltCloudSystemExit("No cinder endpoint available") nt_ks = self.volume_conn volume = nt_ks.volumes.get(volume_id) response = { "name": volume.display_name, "size": volume.size, "id": volume.id, "description": volume.display_description, "attachments": volume.attachments, "status": volume.status, } return response def volume_list(self, search_opts=None): """ List all block volumes """ if self.volume_conn is None: raise SaltCloudSystemExit("No cinder endpoint available") nt_ks = self.volume_conn volumes = nt_ks.volumes.list(search_opts=search_opts) response = {} for volume in volumes: response[volume.display_name] = { "name": volume.display_name, "size": volume.size, "id": volume.id, "description": volume.display_description, "attachments": volume.attachments, "status": volume.status, } return response def volume_show(self, name): """ Show one volume """ if self.volume_conn is None: raise SaltCloudSystemExit("No cinder endpoint available") volumes = self.volume_list( search_opts={"display_name": name}, ) volume = volumes[name] return volume def volume_create( self, name, size=100, snapshot=None, voltype=None, availability_zone=None ): """ Create a block device """ if self.volume_conn is None: raise SaltCloudSystemExit("No cinder endpoint available") nt_ks = self.volume_conn response = nt_ks.volumes.create( size=size, display_name=name, volume_type=voltype, snapshot_id=snapshot, availability_zone=availability_zone, ) return self._volume_get(response.id) def volume_delete(self, name): """ Delete a block device """ if self.volume_conn is None: raise SaltCloudSystemExit("No cinder endpoint available") nt_ks = self.volume_conn try: volume = self.volume_show(name) except KeyError as exc: raise SaltCloudSystemExit(f"Unable to find {name} volume: {exc}") if volume["status"] == "deleted": return volume response = nt_ks.volumes.delete(volume["id"]) return volume def volume_detach(self, name, timeout=300): """ Detach a block device """ try: volume = self.volume_show(name) except KeyError as exc: raise SaltCloudSystemExit(f"Unable to find {name} volume: {exc}") if not volume["attachments"]: return True response = self.compute_conn.volumes.delete_server_volume( volume["attachments"][0]["server_id"], volume["attachments"][0]["id"] ) trycount = 0 start = time.time() while True: trycount += 1 try: response = self._volume_get(volume["id"]) if response["status"] == "available": return response except Exception as exc: # pylint: disable=broad-except log.debug("Volume is detaching: %s", name) time.sleep(1) if time.time() - start > timeout: log.error( "Timed out after %d seconds while waiting for data", timeout ) return False log.debug("Retrying volume_show() (try %d)", trycount) def volume_attach(self, name, server_name, device="/dev/xvdb", timeout=300): """ Attach a block device """ try: volume = self.volume_show(name) except KeyError as exc: raise SaltCloudSystemExit(f"Unable to find {name} volume: {exc}") server = self.server_by_name(server_name) response = self.compute_conn.volumes.create_server_volume( server.id, volume["id"], device=device ) trycount = 0 start = time.time() while True: trycount += 1 try: response = self._volume_get(volume["id"]) if response["status"] == "in-use": return response except Exception as exc: # pylint: disable=broad-except log.debug("Volume is attaching: %s", name) time.sleep(1) if time.time() - start > timeout: log.error( "Timed out after %s seconds while waiting for data", timeout ) return False log.debug("Retrying volume_show() (try %s)", trycount) def suspend(self, instance_id): """ Suspend a server """ nt_ks = self.compute_conn response = nt_ks.servers.suspend(instance_id) return True def resume(self, instance_id): """ Resume a server """ nt_ks = self.compute_conn response = nt_ks.servers.resume(instance_id) return True def lock(self, instance_id): """ Lock an instance """ nt_ks = self.compute_conn response = nt_ks.servers.lock(instance_id) return True def delete(self, instance_id): """ Delete a server """ nt_ks = self.compute_conn response = nt_ks.servers.delete(instance_id) return True def flavor_list(self): """ Return a list of available flavors (nova flavor-list) """ nt_ks = self.compute_conn ret = {} for flavor in nt_ks.flavors.list(): links = {} for link in flavor.links: links[link["rel"]] = link["href"] ret[flavor.name] = { "disk": flavor.disk, "id": flavor.id, "name": flavor.name, "ram": flavor.ram, "swap": flavor.swap, "vcpus": flavor.vcpus, "links": links, } if hasattr(flavor, "rxtx_factor"): ret[flavor.name]["rxtx_factor"] = flavor.rxtx_factor return ret list_sizes = flavor_list def flavor_create( self, name, # pylint: disable=C0103 flavor_id=0, # pylint: disable=C0103 ram=0, disk=0, vcpus=1, ): """ Create a flavor """ nt_ks = self.compute_conn nt_ks.flavors.create( name=name, flavorid=flavor_id, ram=ram, disk=disk, vcpus=vcpus ) return {"name": name, "id": flavor_id, "ram": ram, "disk": disk, "vcpus": vcpus} def flavor_delete(self, flavor_id): # pylint: disable=C0103 """ Delete a flavor """ nt_ks = self.compute_conn nt_ks.flavors.delete(flavor_id) return f"Flavor deleted: {flavor_id}" def keypair_list(self): """ List keypairs """ nt_ks = self.compute_conn ret = {} for keypair in nt_ks.keypairs.list(): ret[keypair.name] = { "name": keypair.name, "fingerprint": keypair.fingerprint, "public_key": keypair.public_key, } return ret def keypair_add(self, name, pubfile=None, pubkey=None): """ Add a keypair """ nt_ks = self.compute_conn if pubfile: with salt.utils.files.fopen(pubfile, "r") as fp_: pubkey = salt.utils.stringutils.to_unicode(fp_.read()) if not pubkey: return False nt_ks.keypairs.create(name, public_key=pubkey) ret = {"name": name, "pubkey": pubkey} return ret def keypair_delete(self, name): """ Delete a keypair """ nt_ks = self.compute_conn nt_ks.keypairs.delete(name) return f"Keypair deleted: {name}" def image_show(self, image_id): """ Show image details and metadata """ nt_ks = self.compute_conn image = nt_ks.images.get(image_id) links = {} for link in image.links: links[link["rel"]] = link["href"] ret = { "name": image.name, "id": image.id, "status": image.status, "progress": image.progress, "created": image.created, "updated": image.updated, "metadata": image.metadata, "links": links, } if hasattr(image, "minDisk"): ret["minDisk"] = image.minDisk if hasattr(image, "minRam"): ret["minRam"] = image.minRam return ret def image_list(self, name=None): """ List server images """ nt_ks = self.compute_conn ret = {} for image in nt_ks.images.list(): links = {} for link in image.links: links[link["rel"]] = link["href"] ret[image.name] = { "name": image.name, "id": image.id, "status": image.status, "progress": image.progress, "created": image.created, "updated": image.updated, "metadata": image.metadata, "links": links, } if hasattr(image, "minDisk"): ret[image.name]["minDisk"] = image.minDisk if hasattr(image, "minRam"): ret[image.name]["minRam"] = image.minRam if name: return {name: ret[name]} return ret list_images = image_list def image_meta_set( self, image_id=None, name=None, **kwargs ): # pylint: disable=C0103 """ Set image metadata """ nt_ks = self.compute_conn if name: for image in nt_ks.images.list(): if image.name == name: image_id = image.id # pylint: disable=C0103 if not image_id: return {"Error": "A valid image name or id was not specified"} nt_ks.images.set_meta(image_id, kwargs) return {image_id: kwargs} def image_meta_delete( self, image_id=None, name=None, keys=None # pylint: disable=C0103 ): """ Delete image metadata """ nt_ks = self.compute_conn if name: for image in nt_ks.images.list(): if image.name == name: image_id = image.id # pylint: disable=C0103 pairs = keys.split(",") if not image_id: return {"Error": "A valid image name or id was not specified"} nt_ks.images.delete_meta(image_id, pairs) return {image_id: f"Deleted: {pairs}"} def server_list(self): """ List servers """ nt_ks = self.compute_conn ret = {} for item in nt_ks.servers.list(): try: ret[item.name] = { "id": item.id, "name": item.name, "state": item.status, "accessIPv4": item.accessIPv4, "accessIPv6": item.accessIPv6, "flavor": {"id": item.flavor["id"], "links": item.flavor["links"]}, "image": { "id": item.image["id"] if item.image else "Boot From Volume", "links": item.image["links"] if item.image else "", }, } except TypeError: pass return ret def server_list_min(self): """ List minimal information about servers """ nt_ks = self.compute_conn ret = {} for item in nt_ks.servers.list(detailed=False): try: ret[item.name] = {"id": item.id, "state": "Running"} except TypeError: pass return ret def server_list_detailed(self): """ Detailed list of servers """ nt_ks = self.compute_conn ret = {} for item in nt_ks.servers.list(): try: ret[item.name] = { "OS-EXT-SRV-ATTR": {}, "OS-EXT-STS": {}, "accessIPv4": item.accessIPv4, "accessIPv6": item.accessIPv6, "addresses": item.addresses, "created": item.created, "flavor": {"id": item.flavor["id"], "links": item.flavor["links"]}, "hostId": item.hostId, "id": item.id, "image": { "id": item.image["id"] if item.image else "Boot From Volume", "links": item.image["links"] if item.image else "", }, "key_name": item.key_name, "links": item.links, "metadata": item.metadata, "name": item.name, "state": item.status, "tenant_id": item.tenant_id, "updated": item.updated, "user_id": item.user_id, } except TypeError: continue ret[item.name]["progress"] = getattr(item, "progress", "0") if hasattr(item.__dict__, "OS-DCF:diskConfig"): ret[item.name]["OS-DCF"] = { "diskConfig": item.__dict__["OS-DCF:diskConfig"] } if hasattr(item.__dict__, "OS-EXT-SRV-ATTR:host"): ret[item.name]["OS-EXT-SRV-ATTR"]["host"] = item.__dict__[ "OS-EXT-SRV-ATTR:host" ] if hasattr(item.__dict__, "OS-EXT-SRV-ATTR:hypervisor_hostname"): ret[item.name]["OS-EXT-SRV-ATTR"]["hypervisor_hostname"] = ( item.__dict__["OS-EXT-SRV-ATTR:hypervisor_hostname"] ) if hasattr(item.__dict__, "OS-EXT-SRV-ATTR:instance_name"): ret[item.name]["OS-EXT-SRV-ATTR"]["instance_name"] = item.__dict__[ "OS-EXT-SRV-ATTR:instance_name" ] if hasattr(item.__dict__, "OS-EXT-STS:power_state"): ret[item.name]["OS-EXT-STS"]["power_state"] = item.__dict__[ "OS-EXT-STS:power_state" ] if hasattr(item.__dict__, "OS-EXT-STS:task_state"): ret[item.name]["OS-EXT-STS"]["task_state"] = item.__dict__[ "OS-EXT-STS:task_state" ] if hasattr(item.__dict__, "OS-EXT-STS:vm_state"): ret[item.name]["OS-EXT-STS"]["vm_state"] = item.__dict__[ "OS-EXT-STS:vm_state" ] if hasattr(item.__dict__, "security_groups"): ret[item.name]["security_groups"] = item.__dict__["security_groups"] return ret def server_show(self, server_id): """ Show details of one server """ ret = {} try: servers = self.server_list_detailed() except AttributeError: raise SaltCloudSystemExit( "Corrupt server in server_list_detailed. Remove corrupt servers." ) for server_name, server in servers.items(): if str(server["id"]) == server_id: ret[server_name] = server return ret def secgroup_create(self, name, description): """ Create a security group """ nt_ks = self.compute_conn nt_ks.security_groups.create(name, description) ret = {"name": name, "description": description} return ret def secgroup_delete(self, name): """ Delete a security group """ nt_ks = self.compute_conn for item in nt_ks.security_groups.list(): if item.name == name: nt_ks.security_groups.delete(item.id) return {name: f"Deleted security group: {name}"} return f"Security group not found: {name}" def secgroup_list(self): """ List security groups """ nt_ks = self.compute_conn ret = {} for item in nt_ks.security_groups.list(): ret[item.name] = { "name": item.name, "description": item.description, "id": item.id, "tenant_id": item.tenant_id, "rules": item.rules, } return ret def _item_list(self): """ List items """ nt_ks = self.compute_conn ret = [] for item in nt_ks.items.list(): ret.append(item.__dict__) return ret def _network_show(self, name, network_lst): """ Parse the returned network list """ for net in network_lst: if net.label == name: return net.__dict__ return {} def network_show(self, name): """ Show network information """ nt_ks = self.compute_conn net_list = nt_ks.networks.list() return self._network_show(name, net_list) def network_list(self): """ List extra private networks """ nt_ks = self.compute_conn return [network.__dict__ for network in nt_ks.networks.list()] def _sanatize_network_params(self, kwargs): """ Sanatize novaclient network parameters """ params = [ "label", "bridge", "bridge_interface", "cidr", "cidr_v6", "dns1", "dns2", "fixed_cidr", "gateway", "gateway_v6", "multi_host", "priority", "project_id", "vlan_start", "vpn_start", ] for variable in list(kwargs): # iterate over a copy, we might delete some if variable not in params: del kwargs[variable] return kwargs def network_create(self, name, **kwargs): """ Create extra private network """ nt_ks = self.compute_conn kwargs["label"] = name kwargs = self._sanatize_network_params(kwargs) net = nt_ks.networks.create(**kwargs) return net.__dict__ def _server_uuid_from_name(self, name): """ Get server uuid from name """ return self.server_list().get(name, {}).get("id", "") def virtual_interface_list(self, name): """ Get virtual interfaces on slice """ nt_ks = self.compute_conn nets = nt_ks.virtual_interfaces.list(self._server_uuid_from_name(name)) return [network.__dict__ for network in nets] def virtual_interface_create(self, name, net_name): """ Add an interfaces to a slice """ nt_ks = self.compute_conn serverid = self._server_uuid_from_name(name) networkid = self.network_show(net_name).get("id", None) if networkid is None: return {net_name: False} nets = nt_ks.virtual_interfaces.create(networkid, serverid) return nets def floating_ip_pool_list(self): """ List all floating IP pools .. versionadded:: 2016.3.0 """ nt_ks = self.compute_conn pools = nt_ks.floating_ip_pools.list() response = {} for pool in pools: response[pool.name] = { "name": pool.name, } return response def floating_ip_list(self): """ List floating IPs .. versionadded:: 2016.3.0 """ nt_ks = self.compute_conn floating_ips = nt_ks.floating_ips.list() response = {} for floating_ip in floating_ips: response[floating_ip.ip] = { "ip": floating_ip.ip, "fixed_ip": floating_ip.fixed_ip, "id": floating_ip.id, "instance_id": floating_ip.instance_id, "pool": floating_ip.pool, } return response def floating_ip_show(self, ip): """ Show info on specific floating IP .. versionadded:: 2016.3.0 """ nt_ks = self.compute_conn floating_ips = nt_ks.floating_ips.list() for floating_ip in floating_ips: if floating_ip.ip == ip: response = { "ip": floating_ip.ip, "fixed_ip": floating_ip.fixed_ip, "id": floating_ip.id, "instance_id": floating_ip.instance_id, "pool": floating_ip.pool, } return response return {} def floating_ip_create(self, pool=None): """ Allocate a floating IP .. versionadded:: 2016.3.0 """ nt_ks = self.compute_conn floating_ip = nt_ks.floating_ips.create(pool) response = { "ip": floating_ip.ip, "fixed_ip": floating_ip.fixed_ip, "id": floating_ip.id, "instance_id": floating_ip.instance_id, "pool": floating_ip.pool, } return response def floating_ip_delete(self, floating_ip): """ De-allocate a floating IP .. versionadded:: 2016.3.0 """ ip = self.floating_ip_show(floating_ip) nt_ks = self.compute_conn return nt_ks.floating_ips.delete(ip) def floating_ip_associate(self, server_name, floating_ip): """ Associate floating IP address to server .. versionadded:: 2016.3.0 """ nt_ks = self.compute_conn server_ = self.server_by_name(server_name) server = nt_ks.servers.get(server_.__dict__["id"]) server.add_floating_ip(floating_ip) return self.floating_ip_list()[floating_ip] def floating_ip_disassociate(self, server_name, floating_ip): """ Disassociate a floating IP from server .. versionadded:: 2016.3.0 """ nt_ks = self.compute_conn server_ = self.server_by_name(server_name) server = nt_ks.servers.get(server_.__dict__["id"]) server.remove_floating_ip(floating_ip) return self.floating_ip_list()[floating_ip] # The following is a list of functions that need to be incorporated in the # nova module. This list should be updated as functions are added. # # absolute-limits Print a list of absolute limits for a user # actions Retrieve server actions. # add-fixed-ip Add new IP address to network. # aggregate-add-host Add the host to the specified aggregate. # aggregate-create Create a new aggregate with the specified details. # aggregate-delete Delete the aggregate by its id. # aggregate-details Show details of the specified aggregate. # aggregate-list Print a list of all aggregates. # aggregate-remove-host # Remove the specified host from the specified aggregate. # aggregate-set-metadata # Update the metadata associated with the aggregate. # aggregate-update Update the aggregate's name and optionally # availability zone. # cloudpipe-create Create a cloudpipe instance for the given project # cloudpipe-list Print a list of all cloudpipe instances. # console-log Get console log output of a server. # credentials Show user credentials returned from auth # describe-resource Show details about a resource # diagnostics Retrieve server diagnostics. # dns-create Create a DNS entry for domain, name and ip. # dns-create-private-domain # Create the specified DNS domain. # dns-create-public-domain # Create the specified DNS domain. # dns-delete Delete the specified DNS entry. # dns-delete-domain Delete the specified DNS domain. # dns-domains Print a list of available dns domains. # dns-list List current DNS entries for domain and ip or domain # and name. # endpoints Discover endpoints that get returned from the # authenticate services # get-vnc-console Get a vnc console to a server. # host-action Perform a power action on a host. # host-update Update host settings. # image-create Create a new image by taking a snapshot of a running # server. # image-delete Delete an image. # live-migration Migrates a running instance to a new machine. # meta Set or Delete metadata on a server. # migrate Migrate a server. # pause Pause a server. # rate-limits Print a list of rate limits for a user # reboot Reboot a server. # rebuild Shutdown, re-image, and re-boot a server. # remove-fixed-ip Remove an IP address from a server. # rename Rename a server. # rescue Rescue a server. # resize Resize a server. # resize-confirm Confirm a previous resize. # resize-revert Revert a previous resize (and return to the previous # VM). # root-password Change the root password for a server. # secgroup-add-group-rule # Add a source group rule to a security group. # secgroup-add-rule Add a rule to a security group. # secgroup-delete-group-rule # Delete a source group rule from a security group. # secgroup-delete-rule # Delete a rule from a security group. # secgroup-list-rules # List rules for a security group. # ssh SSH into a server. # unlock Unlock a server. # unpause Unpause a server. # unrescue Unrescue a server. # usage-list List usage data for all tenants # volume-list List all the volumes. # volume-snapshot-create # Add a new snapshot. # volume-snapshot-delete # Remove a snapshot. # volume-snapshot-list # List all the snapshots. # volume-snapshot-show # Show details about a snapshot. # volume-type-create Create a new volume type. # volume-type-delete Delete a specific flavor # volume-type-list Print a list of available 'volume types'. # x509-create-cert Create x509 cert for a user in tenant # x509-get-root-cert Fetches the x509 root cert.