Mini Shell
"""
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.
Zerion Mini Shell 1.0