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/opennebula.py |
""" OpenNebula Cloud Module ======================= The OpenNebula cloud module is used to control access to an OpenNebula cloud. .. versionadded:: 2014.7.0 :depends: lxml :depends: OpenNebula installation running version ``4.14`` or later. Use of this module requires the ``xml_rpc``, ``user``, and ``password`` parameters to be set. Set up the cloud configuration at ``/etc/salt/cloud.providers`` or ``/etc/salt/cloud.providers.d/opennebula.conf``: .. code-block:: yaml my-opennebula-config: xml_rpc: http://localhost:2633/RPC2 user: oneadmin password: JHGhgsayu32jsa driver: opennebula This driver supports accessing new VM instances via DNS entry instead of IP address. To enable this feature, in the provider or profile file add `fqdn_base` with a value matching the base of your fully-qualified domain name. Example: .. code-block:: yaml my-opennebula-config: [...] fqdn_base: <my.basedomain.com> [...] The driver will prepend the hostname to the fqdn_base and do a DNS lookup to find the IP of the new VM. .. note: Whenever ``data`` is provided as a kwarg to a function and the attribute=value syntax is used, the entire ``data`` value must be wrapped in single or double quotes. If the value given in the attribute=value data string contains multiple words, double quotes *must* be used for the value while the entire data string should be encapsulated in single quotes. Failing to do so will result in an error. Example: .. code-block:: bash salt-cloud -f image_allocate opennebula datastore_name=default \\ data='NAME="My New Image" DESCRIPTION="Description of the image." \\ PATH=/home/one_user/images/image_name.img' salt-cloud -f secgroup_allocate opennebula \\ data="Name = test RULE = [PROTOCOL = TCP, RULE_TYPE = inbound, \\ RANGE = 1000:2000]" """ import logging import os import pprint import time import salt.config as config import salt.utils.data import salt.utils.files from salt.exceptions import ( SaltCloudConfigError, SaltCloudExecutionFailure, SaltCloudExecutionTimeout, SaltCloudNotFound, SaltCloudSystemExit, ) try: import xmlrpc.client # nosec from lxml import etree HAS_XML_LIBS = True except ImportError: HAS_XML_LIBS = False log = logging.getLogger(__name__) __virtualname__ = "opennebula" def __virtual__(): """ Check for OpenNebula configs. """ 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__, ("xml_rpc", "user", "password"), ) def get_dependencies(): """ Warn if dependencies aren't met. """ return config.check_driver_dependencies(__virtualname__, {"lmxl": HAS_XML_LIBS}) def avail_images(call=None): """ Return available OpenNebula images. CLI Example: .. code-block:: bash salt-cloud --list-images opennebula salt-cloud --function avail_images opennebula salt-cloud -f avail_images opennebula """ if call == "action": raise SaltCloudSystemExit( "The avail_images function must be called with " "-f or --function, or with the --list-images option" ) server, user, password = _get_xml_rpc() auth = ":".join([user, password]) image_pool = server.one.imagepool.info(auth, -2, -1, -1)[1] images = {} for image in _get_xml(image_pool): images[image.find("NAME").text] = _xml_to_dict(image) return images def avail_locations(call=None): """ Return available OpenNebula locations. CLI Example: .. code-block:: bash salt-cloud --list-locations opennebula salt-cloud --function avail_locations opennebula salt-cloud -f avail_locations opennebula """ if call == "action": raise SaltCloudSystemExit( "The avail_locations function must be called with " "-f or --function, or with the --list-locations option." ) server, user, password = _get_xml_rpc() auth = ":".join([user, password]) host_pool = server.one.hostpool.info(auth)[1] locations = {} for host in _get_xml(host_pool): locations[host.find("NAME").text] = _xml_to_dict(host) return locations def avail_sizes(call=None): """ Because sizes are built into templates with OpenNebula, there will be no sizes to return here. """ if call == "action": raise SaltCloudSystemExit( "The avail_sizes function must be called with " "-f or --function, or with the --list-sizes option." ) log.warning( "Because sizes are built into templates with OpenNebula, there are no sizes " "to return." ) return {} def list_clusters(call=None): """ Returns a list of clusters in OpenNebula. .. versionadded:: 2016.3.0 CLI Example: .. code-block:: bash salt-cloud -f list_clusters opennebula """ if call == "action": raise SaltCloudSystemExit( "The list_clusters function must be called with -f or --function." ) server, user, password = _get_xml_rpc() auth = ":".join([user, password]) cluster_pool = server.one.clusterpool.info(auth)[1] clusters = {} for cluster in _get_xml(cluster_pool): clusters[cluster.find("NAME").text] = _xml_to_dict(cluster) return clusters def list_datastores(call=None): """ Returns a list of data stores on OpenNebula. .. versionadded:: 2016.3.0 CLI Example: .. code-block:: bash salt-cloud -f list_datastores opennebula """ if call == "action": raise SaltCloudSystemExit( "The list_datastores function must be called with -f or --function." ) server, user, password = _get_xml_rpc() auth = ":".join([user, password]) datastore_pool = server.one.datastorepool.info(auth)[1] datastores = {} for datastore in _get_xml(datastore_pool): datastores[datastore.find("NAME").text] = _xml_to_dict(datastore) return datastores def list_hosts(call=None): """ Returns a list of hosts on OpenNebula. .. versionadded:: 2016.3.0 CLI Example: .. code-block:: bash salt-cloud -f list_hosts opennebula """ if call == "action": raise SaltCloudSystemExit( "The list_hosts function must be called with -f or --function." ) return avail_locations() def list_nodes(call=None): """ Return a list of VMs on OpenNebula. CLI Example: .. code-block:: bash salt-cloud -Q salt-cloud --query salt-cloud --function list_nodes opennebula salt-cloud -f list_nodes opennebula """ if call == "action": raise SaltCloudSystemExit( "The list_nodes function must be called with -f or --function." ) return _list_nodes(full=False) def list_nodes_full(call=None): """ Return a list of the VMs on OpenNebula. CLI Example: .. code-block:: bash salt-cloud -F salt-cloud --full-query salt-cloud --function list_nodes_full opennebula salt-cloud -f list_nodes_full opennebula """ if call == "action": raise SaltCloudSystemExit( "The list_nodes_full function must be called with -f or --function." ) return _list_nodes(full=True) def list_nodes_select(call=None): """ Return a list of the VMs that are on the provider, with select fields. """ if call == "action": raise SaltCloudSystemExit( "The list_nodes_full function must be called with -f or --function." ) return __utils__["cloud.list_nodes_select"]( list_nodes_full("function"), __opts__["query.selection"], call, ) def list_security_groups(call=None): """ Lists all security groups available to the user and the user's groups. .. versionadded:: 2016.3.0 CLI Example: .. code-block:: bash salt-cloud -f list_security_groups opennebula """ if call == "action": raise SaltCloudSystemExit( "The list_security_groups function must be called with -f or --function." ) server, user, password = _get_xml_rpc() auth = ":".join([user, password]) secgroup_pool = server.one.secgrouppool.info(auth, -2, -1, -1)[1] groups = {} for group in _get_xml(secgroup_pool): groups[group.find("NAME").text] = _xml_to_dict(group) return groups def list_templates(call=None): """ Lists all templates available to the user and the user's groups. .. versionadded:: 2016.3.0 CLI Example: .. code-block:: bash salt-cloud -f list_templates opennebula """ if call == "action": raise SaltCloudSystemExit( "The list_templates function must be called with -f or --function." ) server, user, password = _get_xml_rpc() auth = ":".join([user, password]) template_pool = server.one.templatepool.info(auth, -2, -1, -1)[1] templates = {} for template in _get_xml(template_pool): templates[template.find("NAME").text] = _xml_to_dict(template) return templates def list_vns(call=None): """ Lists all virtual networks available to the user and the user's groups. .. versionadded:: 2016.3.0 CLI Example: .. code-block:: bash salt-cloud -f list_vns opennebula """ if call == "action": raise SaltCloudSystemExit( "The list_vns function must be called with -f or --function." ) server, user, password = _get_xml_rpc() auth = ":".join([user, password]) vn_pool = server.one.vnpool.info(auth, -2, -1, -1)[1] vns = {} for v_network in _get_xml(vn_pool): vns[v_network.find("NAME").text] = _xml_to_dict(v_network) return vns def reboot(name, call=None): """ Reboot a VM. .. versionadded:: 2016.3.0 name The name of the VM to reboot. CLI Example: .. code-block:: bash salt-cloud -a reboot my-vm """ if call != "action": raise SaltCloudSystemExit( "The start action must be called with -a or --action." ) log.info("Rebooting node %s", name) return vm_action(name, kwargs={"action": "reboot"}, call=call) def start(name, call=None): """ Start a VM. .. versionadded:: 2016.3.0 name The name of the VM to start. CLI Example: .. code-block:: bash salt-cloud -a start my-vm """ if call != "action": raise SaltCloudSystemExit( "The start action must be called with -a or --action." ) log.info("Starting node %s", name) return vm_action(name, kwargs={"action": "resume"}, call=call) def stop(name, call=None): """ Stop a VM. .. versionadded:: 2016.3.0 name The name of the VM to stop. CLI Example: .. code-block:: bash salt-cloud -a stop my-vm """ if call != "action": raise SaltCloudSystemExit( "The start action must be called with -a or --action." ) log.info("Stopping node %s", name) return vm_action(name, kwargs={"action": "stop"}, call=call) def get_one_version(kwargs=None, call=None): """ Returns the OpenNebula version. .. versionadded:: 2016.3.5 CLI Example: .. code-block:: bash salt-cloud -f get_one_version one_provider_name """ if call == "action": raise SaltCloudSystemExit( "The get_cluster_id function must be called with -f or --function." ) if kwargs is None: kwargs = {} server, user, password = _get_xml_rpc() auth = ":".join([user, password]) return server.one.system.version(auth)[1] def get_cluster_id(kwargs=None, call=None): """ Returns a cluster's ID from the given cluster name. .. versionadded:: 2016.3.0 CLI Example: .. code-block:: bash salt-cloud -f get_cluster_id opennebula name=my-cluster-name """ if call == "action": raise SaltCloudSystemExit( "The get_cluster_id function must be called with -f or --function." ) if kwargs is None: kwargs = {} name = kwargs.get("name", None) if name is None: raise SaltCloudSystemExit("The get_cluster_id function requires a name.") try: ret = list_clusters()[name]["id"] except KeyError: raise SaltCloudSystemExit(f"The cluster '{name}' could not be found") return ret def get_datastore_id(kwargs=None, call=None): """ Returns a data store's ID from the given data store name. .. versionadded:: 2016.3.0 CLI Example: .. code-block:: bash salt-cloud -f get_datastore_id opennebula name=my-datastore-name """ if call == "action": raise SaltCloudSystemExit( "The get_datastore_id function must be called with -f or --function." ) if kwargs is None: kwargs = {} name = kwargs.get("name", None) if name is None: raise SaltCloudSystemExit("The get_datastore_id function requires a name.") try: ret = list_datastores()[name]["id"] except KeyError: raise SaltCloudSystemExit(f"The datastore '{name}' could not be found.") return ret def get_host_id(kwargs=None, call=None): """ Returns a host's ID from the given host name. .. versionadded:: 2016.3.0 CLI Example: .. code-block:: bash salt-cloud -f get_host_id opennebula name=my-host-name """ if call == "action": raise SaltCloudSystemExit( "The get_host_id function must be called with -f or --function." ) if kwargs is None: kwargs = {} name = kwargs.get("name", None) if name is None: raise SaltCloudSystemExit("The get_host_id function requires a name.") try: ret = avail_locations()[name]["id"] except KeyError: raise SaltCloudSystemExit(f"The host '{name}' could not be found") return ret def get_image(vm_): r""" Return the image object to use. vm\_ The VM dictionary for which to obtain an image. """ images = avail_images() vm_image = str( config.get_cloud_config_value("image", vm_, __opts__, search_global=False) ) for image in images: if vm_image in (images[image]["name"], images[image]["id"]): return images[image]["id"] raise SaltCloudNotFound(f"The specified image, '{vm_image}', could not be found.") def get_image_id(kwargs=None, call=None): """ Returns an image's ID from the given image name. .. versionadded:: 2016.3.0 CLI Example: .. code-block:: bash salt-cloud -f get_image_id opennebula name=my-image-name """ if call == "action": raise SaltCloudSystemExit( "The get_image_id function must be called with -f or --function." ) if kwargs is None: kwargs = {} name = kwargs.get("name", None) if name is None: raise SaltCloudSystemExit("The get_image_id function requires a name.") try: ret = avail_images()[name]["id"] except KeyError: raise SaltCloudSystemExit(f"The image '{name}' could not be found") return ret def get_location(vm_): r""" Return the VM's location. vm\_ The VM dictionary for which to obtain a location. """ locations = avail_locations() vm_location = str( config.get_cloud_config_value("location", vm_, __opts__, search_global=False) ) if vm_location == "None": return None for location in locations: if vm_location in (locations[location]["name"], locations[location]["id"]): return locations[location]["id"] raise SaltCloudNotFound( f"The specified location, '{vm_location}', could not be found." ) def get_secgroup_id(kwargs=None, call=None): """ Returns a security group's ID from the given security group name. .. versionadded:: 2016.3.0 CLI Example: .. code-block:: bash salt-cloud -f get_secgroup_id opennebula name=my-secgroup-name """ if call == "action": raise SaltCloudSystemExit( "The get_secgroup_id function must be called with -f or --function." ) if kwargs is None: kwargs = {} name = kwargs.get("name", None) if name is None: raise SaltCloudSystemExit("The get_secgroup_id function requires a 'name'.") try: ret = list_security_groups()[name]["id"] except KeyError: raise SaltCloudSystemExit(f"The security group '{name}' could not be found.") return ret def get_template_image(kwargs=None, call=None): """ Returns a template's image from the given template name. .. versionadded:: 2018.3.0 .. code-block:: bash salt-cloud -f get_template_image opennebula name=my-template-name """ if call == "action": raise SaltCloudSystemExit( "The get_template_image function must be called with -f or --function." ) if kwargs is None: kwargs = {} name = kwargs.get("name", None) if name is None: raise SaltCloudSystemExit("The get_template_image function requires a 'name'.") try: ret = list_templates()[name]["template"]["disk"]["image"] except KeyError: raise SaltCloudSystemExit( f"The image for template '{name}' could not be found." ) return ret def get_template_id(kwargs=None, call=None): """ Returns a template's ID from the given template name. .. versionadded:: 2016.3.0 CLI Example: .. code-block:: bash salt-cloud -f get_template_id opennebula name=my-template-name """ if call == "action": raise SaltCloudSystemExit( "The get_template_id function must be called with -f or --function." ) if kwargs is None: kwargs = {} name = kwargs.get("name", None) if name is None: raise SaltCloudSystemExit("The get_template_id function requires a 'name'.") try: ret = list_templates()[name]["id"] except KeyError: raise SaltCloudSystemExit(f"The template '{name}' could not be found.") return ret def get_template(vm_): r""" Return the template id for a VM. .. versionadded:: 2016.11.0 vm\_ The VM dictionary for which to obtain a template. """ vm_template = str( config.get_cloud_config_value("template", vm_, __opts__, search_global=False) ) try: return list_templates()[vm_template]["id"] except KeyError: raise SaltCloudNotFound( f"The specified template, '{vm_template}', could not be found." ) def get_vm_id(kwargs=None, call=None): """ Returns a virtual machine's ID from the given virtual machine's name. .. versionadded:: 2016.3.0 CLI Example: .. code-block:: bash salt-cloud -f get_vm_id opennebula name=my-vm """ if call == "action": raise SaltCloudSystemExit( "The get_vm_id function must be called with -f or --function." ) if kwargs is None: kwargs = {} name = kwargs.get("name", None) if name is None: raise SaltCloudSystemExit("The get_vm_id function requires a name.") try: ret = list_nodes()[name]["id"] except KeyError: raise SaltCloudSystemExit(f"The VM '{name}' could not be found.") return ret def get_vn_id(kwargs=None, call=None): """ Returns a virtual network's ID from the given virtual network's name. .. versionadded:: 2016.3.0 CLI Example: .. code-block:: bash salt-cloud -f get_vn_id opennebula name=my-vn-name """ if call == "action": raise SaltCloudSystemExit( "The get_vn_id function must be called with -f or --function." ) if kwargs is None: kwargs = {} name = kwargs.get("name", None) if name is None: raise SaltCloudSystemExit("The get_vn_id function requires a name.") try: ret = list_vns()[name]["id"] except KeyError: raise SaltCloudSystemExit(f"The VN '{name}' could not be found.") return ret def _get_device_template(disk, disk_info, template=None): """ Returns the template format to create a disk in open nebula .. versionadded:: 2018.3.0 """ def _require_disk_opts(*args): for arg in args: if arg not in disk_info: raise SaltCloudSystemExit(f"The disk {disk} requires a {arg} argument") _require_disk_opts("disk_type", "size") size = disk_info["size"] disk_type = disk_info["disk_type"] if disk_type == "clone": if "image" in disk_info: clone_image = disk_info["image"] else: clone_image = get_template_image(kwargs={"name": template}) clone_image_id = get_image_id(kwargs={"name": clone_image}) temp = "DISK=[IMAGE={}, IMAGE_ID={}, CLONE=YES, SIZE={}]".format( clone_image, clone_image_id, size ) return temp if disk_type == "volatile": _require_disk_opts("type") v_type = disk_info["type"] temp = f"DISK=[TYPE={v_type}, SIZE={size}]" if v_type == "fs": _require_disk_opts("format") format = disk_info["format"] temp = f"DISK=[TYPE={v_type}, SIZE={size}, FORMAT={format}]" return temp # TODO add persistant disk_type def create(vm_): r""" Create a single VM from a data dict. vm\_ The dictionary use to create a VM. Optional vm\_ dict options for overwriting template: region_id Optional - OpenNebula Zone ID memory Optional - In MB cpu Optional - Percent of host CPU to allocate vcpu Optional - Amount of vCPUs to allocate CLI Example: .. code-block:: bash salt-cloud -p my-opennebula-profile vm_name salt-cloud -p my-opennebula-profile vm_name memory=16384 cpu=2.5 vcpu=16 """ 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 "opennebula", vm_["profile"] ) is False ): return False except AttributeError: pass __utils__["cloud.fire_event"]( "event", "starting create", "salt/cloud/{}/creating".format(vm_["name"]), args=__utils__["cloud.filter_event"]( "creating", vm_, ["name", "profile", "provider", "driver"] ), sock_dir=__opts__["sock_dir"], transport=__opts__["transport"], ) log.info("Creating Cloud VM %s", vm_["name"]) kwargs = { "name": vm_["name"], "template_id": get_template(vm_), "region_id": get_location(vm_), } if "template" in vm_: kwargs["image_id"] = get_template_id({"name": vm_["template"]}) private_networking = config.get_cloud_config_value( "private_networking", vm_, __opts__, search_global=False, default=None ) kwargs["private_networking"] = "true" if private_networking else "false" __utils__["cloud.fire_event"]( "event", "requesting instance", "salt/cloud/{}/requesting".format(vm_["name"]), args={ "kwargs": __utils__["cloud.filter_event"]( "requesting", kwargs, list(kwargs) ), }, sock_dir=__opts__["sock_dir"], ) template = [] if kwargs.get("region_id"): template.append('SCHED_REQUIREMENTS="ID={}"'.format(kwargs.get("region_id"))) if vm_.get("memory"): template.append("MEMORY={}".format(vm_.get("memory"))) if vm_.get("cpu"): template.append("CPU={}".format(vm_.get("cpu"))) if vm_.get("vcpu"): template.append("VCPU={}".format(vm_.get("vcpu"))) if vm_.get("disk"): get_disks = vm_.get("disk") template_name = vm_["image"] for disk in get_disks: template.append( _get_device_template(disk, get_disks[disk], template=template_name) ) if "CLONE" not in str(template): raise SaltCloudSystemExit( "Missing an image disk to clone. Must define a clone disk alongside all" " other disk definitions." ) template_args = "\n".join(template) try: server, user, password = _get_xml_rpc() auth = ":".join([user, password]) cret = server.one.template.instantiate( auth, int(kwargs["template_id"]), kwargs["name"], False, template_args ) if not cret[0]: log.error( "Error creating %s on OpenNebula\n\n" "The following error was returned when trying to " "instantiate the template: %s", vm_["name"], cret[1], # Show the traceback if the debug logging level is enabled exc_info_on_loglevel=logging.DEBUG, ) return False except Exception as exc: # pylint: disable=broad-except log.error( "Error creating %s on OpenNebula\n\n" "The following exception was thrown when trying to " "run the initial deployment: %s", vm_["name"], exc, # Show the traceback if the debug logging level is enabled exc_info_on_loglevel=logging.DEBUG, ) return False fqdn = vm_.get("fqdn_base") if fqdn is not None: fqdn = "{}.{}".format(vm_["name"], fqdn) def __query_node_data(vm_name): node_data = show_instance(vm_name, call="action") if not node_data: # Trigger an error in the wait_for_ip function return False if node_data["state"] == "7": return False if node_data["lcm_state"] == "3": return node_data try: data = __utils__["cloud.wait_for_ip"]( __query_node_data, update_args=(vm_["name"],), timeout=config.get_cloud_config_value( "wait_for_ip_timeout", vm_, __opts__, default=10 * 60 ), interval=config.get_cloud_config_value( "wait_for_ip_interval", vm_, __opts__, default=2 ), ) except (SaltCloudExecutionTimeout, SaltCloudExecutionFailure) as exc: try: # It might be already up, let's destroy it! destroy(vm_["name"]) except SaltCloudSystemExit: pass finally: raise SaltCloudSystemExit(str(exc)) key_filename = config.get_cloud_config_value( "private_key", vm_, __opts__, search_global=False, default=None ) if key_filename is not None and not os.path.isfile(key_filename): raise SaltCloudConfigError( f"The defined key_filename '{key_filename}' does not exist" ) if fqdn: vm_["ssh_host"] = fqdn private_ip = "0.0.0.0" else: try: private_ip = data["private_ips"][0] except KeyError: try: private_ip = data["template"]["nic"]["ip"] except KeyError: # if IPv6 is used try this as last resort # OpenNebula does not yet show ULA address here so take global private_ip = data["template"]["nic"]["ip6_global"] vm_["ssh_host"] = private_ip ssh_username = config.get_cloud_config_value( "ssh_username", vm_, __opts__, default="root" ) vm_["username"] = ssh_username vm_["key_filename"] = key_filename ret = __utils__["cloud.bootstrap"](vm_, __opts__) ret["id"] = data["id"] ret["image"] = vm_["image"] ret["name"] = vm_["name"] ret["size"] = data["template"]["memory"] ret["state"] = data["state"] ret["private_ips"] = private_ip ret["public_ips"] = [] log.info("Created Cloud VM '%s'", vm_["name"]) log.debug("'%s' VM creation details:\n%s", vm_["name"], pprint.pformat(data)) __utils__["cloud.fire_event"]( "event", "created instance", "salt/cloud/{}/created".format(vm_["name"]), args=__utils__["cloud.filter_event"]( "created", vm_, ["name", "profile", "provider", "driver"] ), sock_dir=__opts__["sock_dir"], ) return ret def destroy(name, call=None): """ Destroy a node. Will check termination protection and warn if enabled. name The name of the vm to be destroyed. CLI Example: .. code-block:: bash salt-cloud --destroy vm_name salt-cloud -d vm_name salt-cloud --action destroy vm_name salt-cloud -a destroy vm_name """ 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", args={"name": name}, sock_dir=__opts__["sock_dir"], ) server, user, password = _get_xml_rpc() auth = ":".join([user, password]) data = show_instance(name, call="action") node = server.one.vm.action(auth, "delete", int(data["id"])) __utils__["cloud.fire_event"]( "event", "destroyed instance", f"salt/cloud/{name}/destroyed", args={"name": name}, sock_dir=__opts__["sock_dir"], ) if __opts__.get("update_cachedir", False) is True: __utils__["cloud.delete_minion_cachedir"]( name, _get_active_provider_name().split(":")[0], __opts__ ) data = { "action": "vm.delete", "deleted": node[0], "node_id": node[1], "error_code": node[2], } return data def image_allocate(call=None, kwargs=None): """ Allocates a new image in OpenNebula. .. versionadded:: 2016.3.0 path The path to a file containing the template of the image to allocate. Syntax within the file can be the usual attribute=value or XML. Can be used instead of ``data``. data The data containing the template of the image to allocate. Syntax can be the usual attribute=value or XML. Can be used instead of ``path``. datastore_id The ID of the data-store to be used for the new image. Can be used instead of ``datastore_name``. datastore_name The name of the data-store to be used for the new image. Can be used instead of ``datastore_id``. CLI Example: .. code-block:: bash salt-cloud -f image_allocate opennebula path=/path/to/image_file.txt datastore_id=1 salt-cloud -f image_allocate opennebula datastore_name=default \\ data='NAME="Ubuntu 14.04" PATH="/home/one_user/images/ubuntu_desktop.img" \\ DESCRIPTION="Ubuntu 14.04 for development."' """ if call != "function": raise SaltCloudSystemExit( "The image_allocate function must be called with -f or --function." ) if kwargs is None: kwargs = {} path = kwargs.get("path", None) data = kwargs.get("data", None) datastore_id = kwargs.get("datastore_id", None) datastore_name = kwargs.get("datastore_name", None) if datastore_id: if datastore_name: log.warning( "Both a 'datastore_id' and a 'datastore_name' were provided. " "'datastore_id' will take precedence." ) elif datastore_name: datastore_id = get_datastore_id(kwargs={"name": datastore_name}) else: raise SaltCloudSystemExit( "The image_allocate function requires either a 'datastore_id' or a " "'datastore_name' to be provided." ) if data: if path: log.warning( "Both the 'data' and 'path' arguments were provided. " "'data' will take precedence." ) elif path: with salt.utils.files.fopen(path, mode="r") as rfh: data = rfh.read() else: raise SaltCloudSystemExit( "The image_allocate function requires either a file 'path' or 'data' " "to be provided." ) server, user, password = _get_xml_rpc() auth = ":".join([user, password]) response = server.one.image.allocate(auth, data, int(datastore_id)) ret = { "action": "image.allocate", "allocated": response[0], "image_id": response[1], "error_code": response[2], } return ret def image_clone(call=None, kwargs=None): """ Clones an existing image. .. versionadded:: 2016.3.0 name The name of the new image. image_id The ID of the image to be cloned. Can be used instead of ``image_name``. image_name The name of the image to be cloned. Can be used instead of ``image_id``. CLI Example: .. code-block:: bash salt-cloud -f image_clone opennebula name=my-new-image image_id=10 salt-cloud -f image_clone opennebula name=my-new-image image_name=my-image-to-clone """ if call != "function": raise SaltCloudSystemExit( "The image_clone function must be called with -f or --function." ) if kwargs is None: kwargs = {} name = kwargs.get("name", None) image_id = kwargs.get("image_id", None) image_name = kwargs.get("image_name", None) if name is None: raise SaltCloudSystemExit( "The image_clone function requires a 'name' to be provided." ) if image_id: if image_name: log.warning( "Both the 'image_id' and 'image_name' arguments were provided. " "'image_id' will take precedence." ) elif image_name: image_id = get_image_id(kwargs={"name": image_name}) else: raise SaltCloudSystemExit( "The image_clone function requires either an 'image_id' or an " "'image_name' to be provided." ) server, user, password = _get_xml_rpc() auth = ":".join([user, password]) response = server.one.image.clone(auth, int(image_id), name) data = { "action": "image.clone", "cloned": response[0], "cloned_image_id": response[1], "cloned_image_name": name, "error_code": response[2], } return data def image_delete(call=None, kwargs=None): """ Deletes the given image from OpenNebula. Either a name or an image_id must be supplied. .. versionadded:: 2016.3.0 name The name of the image to delete. Can be used instead of ``image_id``. image_id The ID of the image to delete. Can be used instead of ``name``. CLI Example: .. code-block:: bash salt-cloud -f image_delete opennebula name=my-image salt-cloud --function image_delete opennebula image_id=100 """ if call != "function": raise SaltCloudSystemExit( "The image_delete function must be called with -f or --function." ) if kwargs is None: kwargs = {} name = kwargs.get("name", None) image_id = kwargs.get("image_id", None) if image_id: if name: log.warning( "Both the 'image_id' and 'name' arguments were provided. " "'image_id' will take precedence." ) elif name: image_id = get_image_id(kwargs={"name": name}) else: raise SaltCloudSystemExit( "The image_delete function requires either an 'image_id' or a " "'name' to be provided." ) server, user, password = _get_xml_rpc() auth = ":".join([user, password]) response = server.one.image.delete(auth, int(image_id)) data = { "action": "image.delete", "deleted": response[0], "image_id": response[1], "error_code": response[2], } return data def image_info(call=None, kwargs=None): """ Retrieves information for a given image. Either a name or an image_id must be supplied. .. versionadded:: 2016.3.0 name The name of the image for which to gather information. Can be used instead of ``image_id``. image_id The ID of the image for which to gather information. Can be used instead of ``name``. CLI Example: .. code-block:: bash salt-cloud -f image_info opennebula name=my-image salt-cloud --function image_info opennebula image_id=5 """ if call != "function": raise SaltCloudSystemExit( "The image_info function must be called with -f or --function." ) if kwargs is None: kwargs = {} name = kwargs.get("name", None) image_id = kwargs.get("image_id", None) if image_id: if name: log.warning( "Both the 'image_id' and 'name' arguments were provided. " "'image_id' will take precedence." ) elif name: image_id = get_image_id(kwargs={"name": name}) else: raise SaltCloudSystemExit( "The image_info function requires either a 'name or an 'image_id' " "to be provided." ) server, user, password = _get_xml_rpc() auth = ":".join([user, password]) info = {} response = server.one.image.info(auth, int(image_id))[1] tree = _get_xml(response) info[tree.find("NAME").text] = _xml_to_dict(tree) return info def image_persistent(call=None, kwargs=None): """ Sets the Image as persistent or not persistent. .. versionadded:: 2016.3.0 name The name of the image to set. Can be used instead of ``image_id``. image_id The ID of the image to set. Can be used instead of ``name``. persist A boolean value to set the image as persistent or not. Set to true for persistent, false for non-persistent. CLI Example: .. code-block:: bash salt-cloud -f image_persistent opennebula name=my-image persist=True salt-cloud --function image_persistent opennebula image_id=5 persist=False """ if call != "function": raise SaltCloudSystemExit( "The image_persistent function must be called with -f or --function." ) if kwargs is None: kwargs = {} name = kwargs.get("name", None) persist = kwargs.get("persist", None) image_id = kwargs.get("image_id", None) if persist is None: raise SaltCloudSystemExit( "The image_persistent function requires 'persist' to be set to 'True' " "or 'False'." ) if image_id: if name: log.warning( "Both the 'image_id' and 'name' arguments were provided. " "'image_id' will take precedence." ) elif name: image_id = get_image_id(kwargs={"name": name}) else: raise SaltCloudSystemExit( "The image_persistent function requires either a 'name' or an " "'image_id' to be provided." ) server, user, password = _get_xml_rpc() auth = ":".join([user, password]) response = server.one.image.persistent( auth, int(image_id), salt.utils.data.is_true(persist) ) data = { "action": "image.persistent", "response": response[0], "image_id": response[1], "error_code": response[2], } return data def image_snapshot_delete(call=None, kwargs=None): """ Deletes a snapshot from the image. .. versionadded:: 2016.3.0 image_id The ID of the image from which to delete the snapshot. Can be used instead of ``image_name``. image_name The name of the image from which to delete the snapshot. Can be used instead of ``image_id``. snapshot_id The ID of the snapshot to delete. CLI Example: .. code-block:: bash salt-cloud -f image_snapshot_delete vm_id=106 snapshot_id=45 salt-cloud -f image_snapshot_delete vm_name=my-vm snapshot_id=111 """ if call != "function": raise SaltCloudSystemExit( "The image_snapshot_delete function must be called with -f or --function." ) if kwargs is None: kwargs = {} image_id = kwargs.get("image_id", None) image_name = kwargs.get("image_name", None) snapshot_id = kwargs.get("snapshot_id", None) if snapshot_id is None: raise SaltCloudSystemExit( "The image_snapshot_delete function requires a 'snapshot_id' to be" " provided." ) if image_id: if image_name: log.warning( "Both the 'image_id' and 'image_name' arguments were provided. " "'image_id' will take precedence." ) elif image_name: image_id = get_image_id(kwargs={"name": image_name}) else: raise SaltCloudSystemExit( "The image_snapshot_delete function requires either an 'image_id' " "or a 'image_name' to be provided." ) server, user, password = _get_xml_rpc() auth = ":".join([user, password]) response = server.one.image.snapshotdelete(auth, int(image_id), int(snapshot_id)) data = { "action": "image.snapshotdelete", "deleted": response[0], "snapshot_id": response[1], "error_code": response[2], } return data def image_snapshot_revert(call=None, kwargs=None): """ Reverts an image state to a previous snapshot. .. versionadded:: 2016.3.0 image_id The ID of the image to revert. Can be used instead of ``image_name``. image_name The name of the image to revert. Can be used instead of ``image_id``. snapshot_id The ID of the snapshot to which the image will be reverted. CLI Example: .. code-block:: bash salt-cloud -f image_snapshot_revert vm_id=106 snapshot_id=45 salt-cloud -f image_snapshot_revert vm_name=my-vm snapshot_id=120 """ if call != "function": raise SaltCloudSystemExit( "The image_snapshot_revert function must be called with -f or --function." ) if kwargs is None: kwargs = {} image_id = kwargs.get("image_id", None) image_name = kwargs.get("image_name", None) snapshot_id = kwargs.get("snapshot_id", None) if snapshot_id is None: raise SaltCloudSystemExit( "The image_snapshot_revert function requires a 'snapshot_id' to be" " provided." ) if image_id: if image_name: log.warning( "Both the 'image_id' and 'image_name' arguments were provided. " "'image_id' will take precedence." ) elif image_name: image_id = get_image_id(kwargs={"name": image_name}) else: raise SaltCloudSystemExit( "The image_snapshot_revert function requires either an 'image_id' or " "an 'image_name' to be provided." ) server, user, password = _get_xml_rpc() auth = ":".join([user, password]) response = server.one.image.snapshotrevert(auth, int(image_id), int(snapshot_id)) data = { "action": "image.snapshotrevert", "reverted": response[0], "snapshot_id": response[1], "error_code": response[2], } return data def image_snapshot_flatten(call=None, kwargs=None): """ Flattens the snapshot of an image and discards others. .. versionadded:: 2016.3.0 image_id The ID of the image. Can be used instead of ``image_name``. image_name The name of the image. Can be used instead of ``image_id``. snapshot_id The ID of the snapshot to flatten. CLI Example: .. code-block:: bash salt-cloud -f image_snapshot_flatten vm_id=106 snapshot_id=45 salt-cloud -f image_snapshot_flatten vm_name=my-vm snapshot_id=45 """ if call != "function": raise SaltCloudSystemExit( "The image_snapshot_flatten function must be called with -f or --function." ) if kwargs is None: kwargs = {} image_id = kwargs.get("image_id", None) image_name = kwargs.get("image_name", None) snapshot_id = kwargs.get("snapshot_id", None) if snapshot_id is None: raise SaltCloudSystemExit( "The image_stanpshot_flatten function requires a 'snapshot_id' " "to be provided." ) if image_id: if image_name: log.warning( "Both the 'image_id' and 'image_name' arguments were provided. " "'image_id' will take precedence." ) elif image_name: image_id = get_image_id(kwargs={"name": image_name}) else: raise SaltCloudSystemExit( "The image_snapshot_flatten function requires either an " "'image_id' or an 'image_name' to be provided." ) server, user, password = _get_xml_rpc() auth = ":".join([user, password]) response = server.one.image.snapshotflatten(auth, int(image_id), int(snapshot_id)) data = { "action": "image.snapshotflatten", "flattened": response[0], "snapshot_id": response[1], "error_code": response[2], } return data def image_update(call=None, kwargs=None): """ Replaces the image template contents. .. versionadded:: 2016.3.0 image_id The ID of the image to update. Can be used instead of ``image_name``. image_name The name of the image to update. Can be used instead of ``image_id``. path The path to a file containing the template of the image. Syntax within the file can be the usual attribute=value or XML. Can be used instead of ``data``. data Contains the template of the image. Syntax can be the usual attribute=value or XML. Can be used instead of ``path``. update_type There are two ways to update an image: ``replace`` the whole template or ``merge`` the new template with the existing one. CLI Example: .. code-block:: bash salt-cloud -f image_update opennebula image_id=0 file=/path/to/image_update_file.txt update_type=replace salt-cloud -f image_update opennebula image_name="Ubuntu 14.04" update_type=merge \\ data='NAME="Ubuntu Dev" PATH="/home/one_user/images/ubuntu_desktop.img" \\ DESCRIPTION = "Ubuntu 14.04 for development."' """ if call != "function": raise SaltCloudSystemExit( "The image_allocate function must be called with -f or --function." ) if kwargs is None: kwargs = {} image_id = kwargs.get("image_id", None) image_name = kwargs.get("image_name", None) path = kwargs.get("path", None) data = kwargs.get("data", None) update_type = kwargs.get("update_type", None) update_args = ["replace", "merge"] if update_type is None: raise SaltCloudSystemExit( "The image_update function requires an 'update_type' to be provided." ) if update_type == update_args[0]: update_number = 0 elif update_type == update_args[1]: update_number = 1 else: raise SaltCloudSystemExit( "The update_type argument must be either {} or {}.".format( update_args[0], update_args[1] ) ) if image_id: if image_name: log.warning( "Both the 'image_id' and 'image_name' arguments were provided. " "'image_id' will take precedence." ) elif image_name: image_id = get_image_id(kwargs={"name": image_name}) else: raise SaltCloudSystemExit( "The image_update function requires either an 'image_id' or an " "'image_name' to be provided." ) if data: if path: log.warning( "Both the 'data' and 'path' arguments were provided. " "'data' will take precedence." ) elif path: with salt.utils.files.fopen(path, mode="r") as rfh: data = rfh.read() else: raise SaltCloudSystemExit( "The image_update function requires either 'data' or a file 'path' " "to be provided." ) server, user, password = _get_xml_rpc() auth = ":".join([user, password]) response = server.one.image.update(auth, int(image_id), data, int(update_number)) ret = { "action": "image.update", "updated": response[0], "image_id": response[1], "error_code": response[2], } return ret def show_instance(name, call=None): """ Show the details from OpenNebula concerning a named VM. name The name of the VM for which to display details. call Type of call to use with this function such as ``function``. CLI Example: .. code-block:: bash salt-cloud --action show_instance vm_name salt-cloud -a show_instance vm_name """ if call != "action": raise SaltCloudSystemExit( "The show_instance action must be called with -a or --action." ) node = _get_node(name) __utils__["cloud.cache_node"](node, _get_active_provider_name(), __opts__) return node def secgroup_allocate(call=None, kwargs=None): """ Allocates a new security group in OpenNebula. .. versionadded:: 2016.3.0 path The path to a file containing the template of the security group. Syntax within the file can be the usual attribute=value or XML. Can be used instead of ``data``. data The template data of the security group. Syntax can be the usual attribute=value or XML. Can be used instead of ``path``. CLI Example: .. code-block:: bash salt-cloud -f secgroup_allocate opennebula path=/path/to/secgroup_file.txt salt-cloud -f secgroup_allocate opennebula \\ data="NAME = test RULE = [PROTOCOL = TCP, RULE_TYPE = inbound, \\ RANGE = 1000:2000]" """ if call != "function": raise SaltCloudSystemExit( "The secgroup_allocate function must be called with -f or --function." ) if kwargs is None: kwargs = {} path = kwargs.get("path", None) data = kwargs.get("data", None) if data: if path: log.warning( "Both the 'data' and 'path' arguments were provided. " "'data' will take precedence." ) elif path: with salt.utils.files.fopen(path, mode="r") as rfh: data = rfh.read() else: raise SaltCloudSystemExit( "The secgroup_allocate function requires either 'data' or a file " "'path' to be provided." ) server, user, password = _get_xml_rpc() auth = ":".join([user, password]) response = server.one.secgroup.allocate(auth, data) ret = { "action": "secgroup.allocate", "allocated": response[0], "secgroup_id": response[1], "error_code": response[2], } return ret def secgroup_clone(call=None, kwargs=None): """ Clones an existing security group. .. versionadded:: 2016.3.0 name The name of the new template. secgroup_id The ID of the security group to be cloned. Can be used instead of ``secgroup_name``. secgroup_name The name of the security group to be cloned. Can be used instead of ``secgroup_id``. CLI Example: .. code-block:: bash salt-cloud -f secgroup_clone opennebula name=my-cloned-secgroup secgroup_id=0 salt-cloud -f secgroup_clone opennebula name=my-cloned-secgroup secgroup_name=my-secgroup """ if call != "function": raise SaltCloudSystemExit( "The secgroup_clone function must be called with -f or --function." ) if kwargs is None: kwargs = {} name = kwargs.get("name", None) secgroup_id = kwargs.get("secgroup_id", None) secgroup_name = kwargs.get("secgroup_name", None) if name is None: raise SaltCloudSystemExit( "The secgroup_clone function requires a 'name' to be provided." ) if secgroup_id: if secgroup_name: log.warning( "Both the 'secgroup_id' and 'secgroup_name' arguments were provided. " "'secgroup_id' will take precedence." ) elif secgroup_name: secgroup_id = get_secgroup_id(kwargs={"name": secgroup_name}) else: raise SaltCloudSystemExit( "The secgroup_clone function requires either a 'secgroup_id' or a " "'secgroup_name' to be provided." ) server, user, password = _get_xml_rpc() auth = ":".join([user, password]) response = server.one.secgroup.clone(auth, int(secgroup_id), name) data = { "action": "secgroup.clone", "cloned": response[0], "cloned_secgroup_id": response[1], "cloned_secgroup_name": name, "error_code": response[2], } return data def secgroup_delete(call=None, kwargs=None): """ Deletes the given security group from OpenNebula. Either a name or a secgroup_id must be supplied. .. versionadded:: 2016.3.0 name The name of the security group to delete. Can be used instead of ``secgroup_id``. secgroup_id The ID of the security group to delete. Can be used instead of ``name``. CLI Example: .. code-block:: bash salt-cloud -f secgroup_delete opennebula name=my-secgroup salt-cloud --function secgroup_delete opennebula secgroup_id=100 """ if call != "function": raise SaltCloudSystemExit( "The secgroup_delete function must be called with -f or --function." ) if kwargs is None: kwargs = {} name = kwargs.get("name", None) secgroup_id = kwargs.get("secgroup_id", None) if secgroup_id: if name: log.warning( "Both the 'secgroup_id' and 'name' arguments were provided. " "'secgroup_id' will take precedence." ) elif name: secgroup_id = get_secgroup_id(kwargs={"name": name}) else: raise SaltCloudSystemExit( "The secgroup_delete function requires either a 'name' or a " "'secgroup_id' to be provided." ) server, user, password = _get_xml_rpc() auth = ":".join([user, password]) response = server.one.secgroup.delete(auth, int(secgroup_id)) data = { "action": "secgroup.delete", "deleted": response[0], "secgroup_id": response[1], "error_code": response[2], } return data def secgroup_info(call=None, kwargs=None): """ Retrieves information for the given security group. Either a name or a secgroup_id must be supplied. .. versionadded:: 2016.3.0 name The name of the security group for which to gather information. Can be used instead of ``secgroup_id``. secgroup_id The ID of the security group for which to gather information. Can be used instead of ``name``. CLI Example: .. code-block:: bash salt-cloud -f secgroup_info opennebula name=my-secgroup salt-cloud --function secgroup_info opennebula secgroup_id=5 """ if call != "function": raise SaltCloudSystemExit( "The secgroup_info function must be called with -f or --function." ) if kwargs is None: kwargs = {} name = kwargs.get("name", None) secgroup_id = kwargs.get("secgroup_id", None) if secgroup_id: if name: log.warning( "Both the 'secgroup_id' and 'name' arguments were provided. " "'secgroup_id' will take precedence." ) elif name: secgroup_id = get_secgroup_id(kwargs={"name": name}) else: raise SaltCloudSystemExit( "The secgroup_info function requires either a name or a secgroup_id " "to be provided." ) server, user, password = _get_xml_rpc() auth = ":".join([user, password]) info = {} response = server.one.secgroup.info(auth, int(secgroup_id))[1] tree = _get_xml(response) info[tree.find("NAME").text] = _xml_to_dict(tree) return info def secgroup_update(call=None, kwargs=None): """ Replaces the security group template contents. .. versionadded:: 2016.3.0 secgroup_id The ID of the security group to update. Can be used instead of ``secgroup_name``. secgroup_name The name of the security group to update. Can be used instead of ``secgroup_id``. path The path to a file containing the template of the security group. Syntax within the file can be the usual attribute=value or XML. Can be used instead of ``data``. data The template data of the security group. Syntax can be the usual attribute=value or XML. Can be used instead of ``path``. update_type There are two ways to update a security group: ``replace`` the whole template or ``merge`` the new template with the existing one. CLI Example: .. code-block:: bash salt-cloud --function secgroup_update opennebula secgroup_id=100 \\ path=/path/to/secgroup_update_file.txt \\ update_type=replace salt-cloud -f secgroup_update opennebula secgroup_name=my-secgroup update_type=merge \\ data="Name = test RULE = [PROTOCOL = TCP, RULE_TYPE = inbound, RANGE = 1000:2000]" """ if call != "function": raise SaltCloudSystemExit( "The secgroup_allocate function must be called with -f or --function." ) if kwargs is None: kwargs = {} secgroup_id = kwargs.get("secgroup_id", None) secgroup_name = kwargs.get("secgroup_name", None) path = kwargs.get("path", None) data = kwargs.get("data", None) update_type = kwargs.get("update_type", None) update_args = ["replace", "merge"] if update_type is None: raise SaltCloudSystemExit( "The secgroup_update function requires an 'update_type' to be provided." ) if update_type == update_args[0]: update_number = 0 elif update_type == update_args[1]: update_number = 1 else: raise SaltCloudSystemExit( "The update_type argument must be either {} or {}.".format( update_args[0], update_args[1] ) ) if secgroup_id: if secgroup_name: log.warning( "Both the 'secgroup_id' and 'secgroup_name' arguments were provided. " "'secgroup_id' will take precedence." ) elif secgroup_name: secgroup_id = get_secgroup_id(kwargs={"name": secgroup_name}) else: raise SaltCloudSystemExit( "The secgroup_update function requires either a 'secgroup_id' or a " "'secgroup_name' to be provided." ) if data: if path: log.warning( "Both the 'data' and 'path' arguments were provided. " "'data' will take precedence." ) elif path: with salt.utils.files.fopen(path, mode="r") as rfh: data = rfh.read() else: raise SaltCloudSystemExit( "The secgroup_update function requires either 'data' or a file 'path' " "to be provided." ) server, user, password = _get_xml_rpc() auth = ":".join([user, password]) response = server.one.secgroup.update( auth, int(secgroup_id), data, int(update_number) ) ret = { "action": "secgroup.update", "updated": response[0], "secgroup_id": response[1], "error_code": response[2], } return ret def template_allocate(call=None, kwargs=None): """ Allocates a new template in OpenNebula. .. versionadded:: 2016.3.0 path The path to a file containing the elements of the template to be allocated. Syntax within the file can be the usual attribute=value or XML. Can be used instead of ``data``. data Contains the elements of the template to be allocated. Syntax can be the usual attribute=value or XML. Can be used instead of ``path``. CLI Example: .. code-block:: bash salt-cloud -f template_allocate opennebula path=/path/to/template_file.txt salt-cloud -f template_allocate opennebula \\ data='CPU="1.0" DISK=[IMAGE="Ubuntu-14.04"] GRAPHICS=[LISTEN="0.0.0.0",TYPE="vnc"] \\ MEMORY="1024" NETWORK="yes" NIC=[NETWORK="192net",NETWORK_UNAME="oneadmin"] \\ OS=[ARCH="x86_64"] SUNSTONE_CAPACITY_SELECT="YES" SUNSTONE_NETWORK_SELECT="YES" \\ VCPU="1"' """ if call != "function": raise SaltCloudSystemExit( "The template_allocate function must be called with -f or --function." ) if kwargs is None: kwargs = {} path = kwargs.get("path", None) data = kwargs.get("data", None) if data: if path: log.warning( "Both the 'data' and 'path' arguments were provided. " "'data' will take precedence." ) elif path: with salt.utils.files.fopen(path, mode="r") as rfh: data = rfh.read() else: raise SaltCloudSystemExit( "The template_allocate function requires either 'data' or a file " "'path' to be provided." ) server, user, password = _get_xml_rpc() auth = ":".join([user, password]) response = server.one.template.allocate(auth, data) ret = { "action": "template.allocate", "allocated": response[0], "template_id": response[1], "error_code": response[2], } return ret def template_clone(call=None, kwargs=None): """ Clones an existing virtual machine template. .. versionadded:: 2016.3.0 name The name of the new template. template_id The ID of the template to be cloned. Can be used instead of ``template_name``. template_name The name of the template to be cloned. Can be used instead of ``template_id``. clone_images Optional, defaults to False. Indicates if the images attached to the template should be cloned as well. CLI Example: .. code-block:: bash salt-cloud -f template_clone opennebula name=my-new-template template_id=0 salt-cloud -f template_clone opennebula name=my-new-template template_name=my-template """ if call != "function": raise SaltCloudSystemExit( "The template_clone function must be called with -f or --function." ) if kwargs is None: kwargs = {} name = kwargs.get("name", None) template_id = kwargs.get("template_id", None) template_name = kwargs.get("template_name", None) clone_images = kwargs.get("clone_images", False) if name is None: raise SaltCloudSystemExit( "The template_clone function requires a name to be provided." ) if template_id: if template_name: log.warning( "Both the 'template_id' and 'template_name' arguments were provided. " "'template_id' will take precedence." ) elif template_name: template_id = get_template_id(kwargs={"name": template_name}) else: raise SaltCloudSystemExit( "The template_clone function requires either a 'template_id' " "or a 'template_name' to be provided." ) server, user, password = _get_xml_rpc() auth = ":".join([user, password]) response = server.one.template.clone(auth, int(template_id), name, clone_images) data = { "action": "template.clone", "cloned": response[0], "cloned_template_id": response[1], "cloned_template_name": name, "error_code": response[2], } return data def template_delete(call=None, kwargs=None): """ Deletes the given template from OpenNebula. Either a name or a template_id must be supplied. .. versionadded:: 2016.3.0 name The name of the template to delete. Can be used instead of ``template_id``. template_id The ID of the template to delete. Can be used instead of ``name``. CLI Example: .. code-block:: bash salt-cloud -f template_delete opennebula name=my-template salt-cloud --function template_delete opennebula template_id=5 """ if call != "function": raise SaltCloudSystemExit( "The template_delete function must be called with -f or --function." ) if kwargs is None: kwargs = {} name = kwargs.get("name", None) template_id = kwargs.get("template_id", None) if template_id: if name: log.warning( "Both the 'template_id' and 'name' arguments were provided. " "'template_id' will take precedence." ) elif name: template_id = get_template_id(kwargs={"name": name}) else: raise SaltCloudSystemExit( "The template_delete function requires either a 'name' or a 'template_id' " "to be provided." ) server, user, password = _get_xml_rpc() auth = ":".join([user, password]) response = server.one.template.delete(auth, int(template_id)) data = { "action": "template.delete", "deleted": response[0], "template_id": response[1], "error_code": response[2], } return data def template_instantiate(call=None, kwargs=None): """ Instantiates a new virtual machine from a template. .. versionadded:: 2016.3.0 .. note:: ``template_instantiate`` creates a VM on OpenNebula from a template, but it does not install Salt on the new VM. Use the ``create`` function for that functionality: ``salt-cloud -p opennebula-profile vm-name``. vm_name Name for the new VM instance. template_id The ID of the template from which the VM will be created. Can be used instead of ``template_name``. template_name The name of the template from which the VM will be created. Can be used instead of ``template_id``. CLI Example: .. code-block:: bash salt-cloud -f template_instantiate opennebula vm_name=my-new-vm template_id=0 """ if call != "function": raise SaltCloudSystemExit( "The template_instantiate function must be called with -f or --function." ) if kwargs is None: kwargs = {} vm_name = kwargs.get("vm_name", None) template_id = kwargs.get("template_id", None) template_name = kwargs.get("template_name", None) if vm_name is None: raise SaltCloudSystemExit( "The template_instantiate function requires a 'vm_name' to be provided." ) if template_id: if template_name: log.warning( "Both the 'template_id' and 'template_name' arguments were provided. " "'template_id' will take precedence." ) elif template_name: template_id = get_template_id(kwargs={"name": template_name}) else: raise SaltCloudSystemExit( "The template_instantiate function requires either a 'template_id' " "or a 'template_name' to be provided." ) server, user, password = _get_xml_rpc() auth = ":".join([user, password]) response = server.one.template.instantiate(auth, int(template_id), vm_name) data = { "action": "template.instantiate", "instantiated": response[0], "instantiated_vm_id": response[1], "vm_name": vm_name, "error_code": response[2], } return data def template_update(call=None, kwargs=None): """ Replaces the template contents. .. versionadded:: 2016.3.0 template_id The ID of the template to update. Can be used instead of ``template_name``. template_name The name of the template to update. Can be used instead of ``template_id``. path The path to a file containing the elements of the template to be updated. Syntax within the file can be the usual attribute=value or XML. Can be used instead of ``data``. data Contains the elements of the template to be updated. Syntax can be the usual attribute=value or XML. Can be used instead of ``path``. update_type There are two ways to update a template: ``replace`` the whole template or ``merge`` the new template with the existing one. CLI Example: .. code-block:: bash salt-cloud --function template_update opennebula template_id=1 update_type=replace \\ path=/path/to/template_update_file.txt salt-cloud -f template_update opennebula template_name=my-template update_type=merge \\ data='CPU="1.0" DISK=[IMAGE="Ubuntu-14.04"] GRAPHICS=[LISTEN="0.0.0.0",TYPE="vnc"] \\ MEMORY="1024" NETWORK="yes" NIC=[NETWORK="192net",NETWORK_UNAME="oneadmin"] \\ OS=[ARCH="x86_64"] SUNSTONE_CAPACITY_SELECT="YES" SUNSTONE_NETWORK_SELECT="YES" \\ VCPU="1"' """ if call != "function": raise SaltCloudSystemExit( "The template_update function must be called with -f or --function." ) if kwargs is None: kwargs = {} template_id = kwargs.get("template_id", None) template_name = kwargs.get("template_name", None) path = kwargs.get("path", None) data = kwargs.get("data", None) update_type = kwargs.get("update_type", None) update_args = ["replace", "merge"] if update_type is None: raise SaltCloudSystemExit( "The template_update function requires an 'update_type' to be provided." ) if update_type == update_args[0]: update_number = 0 elif update_type == update_args[1]: update_number = 1 else: raise SaltCloudSystemExit( "The update_type argument must be either {} or {}.".format( update_args[0], update_args[1] ) ) if template_id: if template_name: log.warning( "Both the 'template_id' and 'template_name' arguments were provided. " "'template_id' will take precedence." ) elif template_name: template_id = get_template_id(kwargs={"name": template_name}) else: raise SaltCloudSystemExit( "The template_update function requires either a 'template_id' " "or a 'template_name' to be provided." ) if data: if path: log.warning( "Both the 'data' and 'path' arguments were provided. " "'data' will take precedence." ) elif path: with salt.utils.files.fopen(path, mode="r") as rfh: data = rfh.read() else: raise SaltCloudSystemExit( "The template_update function requires either 'data' or a file " "'path' to be provided." ) server, user, password = _get_xml_rpc() auth = ":".join([user, password]) response = server.one.template.update( auth, int(template_id), data, int(update_number) ) ret = { "action": "template.update", "updated": response[0], "template_id": response[1], "error_code": response[2], } return ret def vm_action(name, kwargs=None, call=None): """ Submits an action to be performed on a given virtual machine. .. versionadded:: 2016.3.0 name The name of the VM to action. action The action to be performed on the VM. Available options include: - boot - delete - delete-recreate - hold - poweroff - poweroff-hard - reboot - reboot-hard - release - resched - resume - shutdown - shutdown-hard - stop - suspend - undeploy - undeploy-hard - unresched CLI Example: .. code-block:: bash salt-cloud -a vm_action my-vm action='release' """ if call != "action": raise SaltCloudSystemExit( "The vm_action function must be called with -a or --action." ) if kwargs is None: kwargs = {} action = kwargs.get("action", None) if action is None: raise SaltCloudSystemExit( "The vm_action function must have an 'action' provided." ) server, user, password = _get_xml_rpc() auth = ":".join([user, password]) vm_id = int(get_vm_id(kwargs={"name": name})) response = server.one.vm.action(auth, action, vm_id) data = { "action": "vm.action." + str(action), "actioned": response[0], "vm_id": response[1], "error_code": response[2], } return data def vm_allocate(call=None, kwargs=None): """ Allocates a new virtual machine in OpenNebula. .. versionadded:: 2016.3.0 path The path to a file defining the template of the VM to allocate. Syntax within the file can be the usual attribute=value or XML. Can be used instead of ``data``. data Contains the template definitions of the VM to allocate. Syntax can be the usual attribute=value or XML. Can be used instead of ``path``. hold If this parameter is set to ``True``, the VM will be created in the ``HOLD`` state. If not set, the VM is created in the ``PENDING`` state. Default is ``False``. CLI Example: .. code-block:: bash salt-cloud -f vm_allocate path=/path/to/vm_template.txt salt-cloud --function vm_allocate path=/path/to/vm_template.txt hold=True """ if call != "function": raise SaltCloudSystemExit( "The vm_allocate function must be called with -f or --function." ) if kwargs is None: kwargs = {} path = kwargs.get("path", None) data = kwargs.get("data", None) hold = kwargs.get("hold", False) if data: if path: log.warning( "Both the 'data' and 'path' arguments were provided. " "'data' will take precedence." ) elif path: with salt.utils.files.fopen(path, mode="r") as rfh: data = rfh.read() else: raise SaltCloudSystemExit( "The vm_allocate function requires either 'data' or a file 'path' " "to be provided." ) server, user, password = _get_xml_rpc() auth = ":".join([user, password]) response = server.one.vm.allocate(auth, data, salt.utils.data.is_true(hold)) ret = { "action": "vm.allocate", "allocated": response[0], "vm_id": response[1], "error_code": response[2], } return ret def vm_attach(name, kwargs=None, call=None): """ Attaches a new disk to the given virtual machine. .. versionadded:: 2016.3.0 name The name of the VM for which to attach the new disk. path The path to a file containing a single disk vector attribute. Syntax within the file can be the usual attribute=value or XML. Can be used instead of ``data``. data Contains the data needed to attach a single disk vector attribute. Syntax can be the usual attribute=value or XML. Can be used instead of ``path``. CLI Example: .. code-block:: bash salt-cloud -a vm_attach my-vm path=/path/to/disk_file.txt salt-cloud -a vm_attach my-vm data="DISK=[DISK_ID=1]" """ if call != "action": raise SaltCloudSystemExit( "The vm_attach action must be called with -a or --action." ) if kwargs is None: kwargs = {} path = kwargs.get("path", None) data = kwargs.get("data", None) if data: if path: log.warning( "Both the 'data' and 'path' arguments were provided. " "'data' will take precedence." ) elif path: with salt.utils.files.fopen(path, mode="r") as rfh: data = rfh.read() else: raise SaltCloudSystemExit( "The vm_attach function requires either 'data' or a file " "'path' to be provided." ) server, user, password = _get_xml_rpc() auth = ":".join([user, password]) vm_id = int(get_vm_id(kwargs={"name": name})) response = server.one.vm.attach(auth, vm_id, data) ret = { "action": "vm.attach", "attached": response[0], "vm_id": response[1], "error_code": response[2], } return ret def vm_attach_nic(name, kwargs=None, call=None): """ Attaches a new network interface to the given virtual machine. .. versionadded:: 2016.3.0 name The name of the VM for which to attach the new network interface. path The path to a file containing a single NIC vector attribute. Syntax within the file can be the usual attribute=value or XML. Can be used instead of ``data``. data Contains the single NIC vector attribute to attach to the VM. Syntax can be the usual attribute=value or XML. Can be used instead of ``path``. CLI Example: .. code-block:: bash salt-cloud -a vm_attach_nic my-vm path=/path/to/nic_file.txt salt-cloud -a vm_attach_nic my-vm data="NIC=[NETWORK_ID=1]" """ if call != "action": raise SaltCloudSystemExit( "The vm_attach_nic action must be called with -a or --action." ) if kwargs is None: kwargs = {} path = kwargs.get("path", None) data = kwargs.get("data", None) if data: if path: log.warning( "Both the 'data' and 'path' arguments were provided. " "'data' will take precedence." ) elif path: with salt.utils.files.fopen(path, mode="r") as rfh: data = rfh.read() else: raise SaltCloudSystemExit( "The vm_attach_nic function requires either 'data' or a file " "'path' to be provided." ) server, user, password = _get_xml_rpc() auth = ":".join([user, password]) vm_id = int(get_vm_id(kwargs={"name": name})) response = server.one.vm.attachnic(auth, vm_id, data) ret = { "action": "vm.attachnic", "nic_attached": response[0], "vm_id": response[1], "error_code": response[2], } return ret def vm_deploy(name, kwargs=None, call=None): """ Initiates the instance of the given VM on the target host. .. versionadded:: 2016.3.0 name The name of the VM to deploy. host_id The ID of the target host where the VM will be deployed. Can be used instead of ``host_name``. host_name The name of the target host where the VM will be deployed. Can be used instead of ``host_id``. capacity_maintained True to enforce the Host capacity is not over-committed. This parameter is only acknowledged for users in the ``oneadmin`` group. Host capacity will be always enforced for regular users. datastore_id The ID of the target system data-store where the VM will be deployed. Optional and can be used instead of ``datastore_name``. If neither ``datastore_id`` nor ``datastore_name`` are set, OpenNebula will choose the data-store. datastore_name The name of the target system data-store where the VM will be deployed. Optional, and can be used instead of ``datastore_id``. If neither ``datastore_id`` nor ``datastore_name`` are set, OpenNebula will choose the data-store. CLI Example: .. code-block:: bash salt-cloud -a vm_deploy my-vm host_id=0 salt-cloud -a vm_deploy my-vm host_id=1 capacity_maintained=False salt-cloud -a vm_deploy my-vm host_name=host01 datastore_id=1 salt-cloud -a vm_deploy my-vm host_name=host01 datastore_name=default """ if call != "action": raise SaltCloudSystemExit( "The vm_deploy action must be called with -a or --action." ) if kwargs is None: kwargs = {} host_id = kwargs.get("host_id", None) host_name = kwargs.get("host_name", None) capacity_maintained = kwargs.get("capacity_maintained", True) datastore_id = kwargs.get("datastore_id", None) datastore_name = kwargs.get("datastore_name", None) if host_id: if host_name: log.warning( "Both the 'host_id' and 'host_name' arguments were provided. " "'host_id' will take precedence." ) elif host_name: host_id = get_host_id(kwargs={"name": host_name}) else: raise SaltCloudSystemExit( "The vm_deploy function requires a 'host_id' or a 'host_name' " "to be provided." ) if datastore_id: if datastore_name: log.warning( "Both the 'datastore_id' and 'datastore_name' arguments were provided. " "'datastore_id' will take precedence." ) elif datastore_name: datastore_id = get_datastore_id(kwargs={"name": datastore_name}) else: datastore_id = "-1" server, user, password = _get_xml_rpc() auth = ":".join([user, password]) vm_id = get_vm_id(kwargs={"name": name}) response = server.one.vm.deploy( auth, int(vm_id), int(host_id), salt.utils.data.is_true(capacity_maintained), int(datastore_id), ) data = { "action": "vm.deploy", "deployed": response[0], "vm_id": response[1], "error_code": response[2], } return data def vm_detach(name, kwargs=None, call=None): """ Detaches a disk from a virtual machine. .. versionadded:: 2016.3.0 name The name of the VM from which to detach the disk. disk_id The ID of the disk to detach. CLI Example: .. code-block:: bash salt-cloud -a vm_detach my-vm disk_id=1 """ if call != "action": raise SaltCloudSystemExit( "The vm_detach action must be called with -a or --action." ) if kwargs is None: kwargs = {} disk_id = kwargs.get("disk_id", None) if disk_id is None: raise SaltCloudSystemExit( "The vm_detach function requires a 'disk_id' to be provided." ) server, user, password = _get_xml_rpc() auth = ":".join([user, password]) vm_id = int(get_vm_id(kwargs={"name": name})) response = server.one.vm.detach(auth, vm_id, int(disk_id)) data = { "action": "vm.detach", "detached": response[0], "vm_id": response[1], "error_code": response[2], } return data def vm_detach_nic(name, kwargs=None, call=None): """ Detaches a disk from a virtual machine. .. versionadded:: 2016.3.0 name The name of the VM from which to detach the network interface. nic_id The ID of the nic to detach. CLI Example: .. code-block:: bash salt-cloud -a vm_detach_nic my-vm nic_id=1 """ if call != "action": raise SaltCloudSystemExit( "The vm_detach_nic action must be called with -a or --action." ) if kwargs is None: kwargs = {} nic_id = kwargs.get("nic_id", None) if nic_id is None: raise SaltCloudSystemExit( "The vm_detach_nic function requires a 'nic_id' to be provided." ) server, user, password = _get_xml_rpc() auth = ":".join([user, password]) vm_id = int(get_vm_id(kwargs={"name": name})) response = server.one.vm.detachnic(auth, vm_id, int(nic_id)) data = { "action": "vm.detachnic", "nic_detached": response[0], "vm_id": response[1], "error_code": response[2], } return data def vm_disk_save(name, kwargs=None, call=None): """ Sets the disk to be saved in the given image. .. versionadded:: 2016.3.0 name The name of the VM containing the disk to save. disk_id The ID of the disk to save. image_name The name of the new image where the disk will be saved. image_type The type for the new image. If not set, then the default ``ONED`` Configuration will be used. Other valid types include: OS, CDROM, DATABLOCK, KERNEL, RAMDISK, and CONTEXT. snapshot_id The ID of the snapshot to export. If not set, the current image state will be used. CLI Example: .. code-block:: bash salt-cloud -a vm_disk_save my-vm disk_id=1 image_name=my-new-image salt-cloud -a vm_disk_save my-vm disk_id=1 image_name=my-new-image image_type=CONTEXT snapshot_id=10 """ if call != "action": raise SaltCloudSystemExit( "The vm_disk_save action must be called with -a or --action." ) if kwargs is None: kwargs = {} disk_id = kwargs.get("disk_id", None) image_name = kwargs.get("image_name", None) image_type = kwargs.get("image_type", "") snapshot_id = int(kwargs.get("snapshot_id", "-1")) if disk_id is None or image_name is None: raise SaltCloudSystemExit( "The vm_disk_save function requires a 'disk_id' and an 'image_name' " "to be provided." ) server, user, password = _get_xml_rpc() auth = ":".join([user, password]) vm_id = int(get_vm_id(kwargs={"name": name})) response = server.one.vm.disksave( auth, vm_id, int(disk_id), image_name, image_type, snapshot_id ) data = { "action": "vm.disksave", "saved": response[0], "image_id": response[1], "error_code": response[2], } return data def vm_disk_snapshot_create(name, kwargs=None, call=None): """ Takes a new snapshot of the disk image. .. versionadded:: 2016.3.0 name The name of the VM of which to take the snapshot. disk_id The ID of the disk to save. description The description for the snapshot. CLI Example: .. code-block:: bash salt-cloud -a vm_disk_snapshot_create my-vm disk_id=0 description="My Snapshot Description" """ if call != "action": raise SaltCloudSystemExit( "The vm_disk_snapshot_create action must be called with -a or --action." ) if kwargs is None: kwargs = {} disk_id = kwargs.get("disk_id", None) description = kwargs.get("description", None) if disk_id is None or description is None: raise SaltCloudSystemExit( "The vm_disk_snapshot_create function requires a 'disk_id' and a" " 'description' to be provided." ) server, user, password = _get_xml_rpc() auth = ":".join([user, password]) vm_id = int(get_vm_id(kwargs={"name": name})) response = server.one.vm.disksnapshotcreate(auth, vm_id, int(disk_id), description) data = { "action": "vm.disksnapshotcreate", "created": response[0], "snapshot_id": response[1], "error_code": response[2], } return data def vm_disk_snapshot_delete(name, kwargs=None, call=None): """ Deletes a disk snapshot based on the given VM and the disk_id. .. versionadded:: 2016.3.0 name The name of the VM containing the snapshot to delete. disk_id The ID of the disk to save. snapshot_id The ID of the snapshot to be deleted. CLI Example: .. code-block:: bash salt-cloud -a vm_disk_snapshot_delete my-vm disk_id=0 snapshot_id=6 """ if call != "action": raise SaltCloudSystemExit( "The vm_disk_snapshot_delete action must be called with -a or --action." ) if kwargs is None: kwargs = {} disk_id = kwargs.get("disk_id", None) snapshot_id = kwargs.get("snapshot_id", None) if disk_id is None or snapshot_id is None: raise SaltCloudSystemExit( "The vm_disk_snapshot_create function requires a 'disk_id' and a" " 'snapshot_id' to be provided." ) server, user, password = _get_xml_rpc() auth = ":".join([user, password]) vm_id = int(get_vm_id(kwargs={"name": name})) response = server.one.vm.disksnapshotdelete( auth, vm_id, int(disk_id), int(snapshot_id) ) data = { "action": "vm.disksnapshotdelete", "deleted": response[0], "snapshot_id": response[1], "error_code": response[2], } return data def vm_disk_snapshot_revert(name, kwargs=None, call=None): """ Reverts a disk state to a previously taken snapshot. .. versionadded:: 2016.3.0 name The name of the VM containing the snapshot. disk_id The ID of the disk to revert its state. snapshot_id The ID of the snapshot to which the snapshot should be reverted. CLI Example: .. code-block:: bash salt-cloud -a vm_disk_snapshot_revert my-vm disk_id=0 snapshot_id=6 """ if call != "action": raise SaltCloudSystemExit( "The vm_disk_snapshot_revert action must be called with -a or --action." ) if kwargs is None: kwargs = {} disk_id = kwargs.get("disk_id", None) snapshot_id = kwargs.get("snapshot_id", None) if disk_id is None or snapshot_id is None: raise SaltCloudSystemExit( "The vm_disk_snapshot_revert function requires a 'disk_id' and a" " 'snapshot_id' to be provided." ) server, user, password = _get_xml_rpc() auth = ":".join([user, password]) vm_id = int(get_vm_id(kwargs={"name": name})) response = server.one.vm.disksnapshotrevert( auth, vm_id, int(disk_id), int(snapshot_id) ) data = { "action": "vm.disksnapshotrevert", "deleted": response[0], "snapshot_id": response[1], "error_code": response[2], } return data def vm_info(name, call=None): """ Retrieves information for a given virtual machine. A VM name must be supplied. .. versionadded:: 2016.3.0 name The name of the VM for which to gather information. CLI Example: .. code-block:: bash salt-cloud -a vm_info my-vm """ if call != "action": raise SaltCloudSystemExit( "The vm_info action must be called with -a or --action." ) server, user, password = _get_xml_rpc() auth = ":".join([user, password]) vm_id = int(get_vm_id(kwargs={"name": name})) response = server.one.vm.info(auth, vm_id) if response[0] is False: return response[1] else: info = {} tree = _get_xml(response[1]) info[tree.find("NAME").text] = _xml_to_dict(tree) return info def vm_migrate(name, kwargs=None, call=None): """ Migrates the specified virtual machine to the specified target host. .. versionadded:: 2016.3.0 name The name of the VM to migrate. host_id The ID of the host to which the VM will be migrated. Can be used instead of ``host_name``. host_name The name of the host to which the VM will be migrated. Can be used instead of ``host_id``. live_migration If set to ``True``, a live-migration will be performed. Default is ``False``. capacity_maintained True to enforce the Host capacity is not over-committed. This parameter is only acknowledged for users in the ``oneadmin`` group. Host capacity will be always enforced for regular users. datastore_id The target system data-store ID where the VM will be migrated. Can be used instead of ``datastore_name``. datastore_name The name of the data-store target system where the VM will be migrated. Can be used instead of ``datastore_id``. CLI Example: .. code-block:: bash salt-cloud -a vm_migrate my-vm host_id=0 datastore_id=1 salt-cloud -a vm_migrate my-vm host_id=0 datastore_id=1 live_migration=True salt-cloud -a vm_migrate my-vm host_name=host01 datastore_name=default """ if call != "action": raise SaltCloudSystemExit( "The vm_migrate action must be called with -a or --action." ) if kwargs is None: kwargs = {} host_id = kwargs.get("host_id", None) host_name = kwargs.get("host_name", None) live_migration = kwargs.get("live_migration", False) capacity_maintained = kwargs.get("capacity_maintained", True) datastore_id = kwargs.get("datastore_id", None) datastore_name = kwargs.get("datastore_name", None) if datastore_id: if datastore_name: log.warning( "Both the 'datastore_id' and 'datastore_name' arguments were provided. " "'datastore_id' will take precedence." ) elif datastore_name: datastore_id = get_datastore_id(kwargs={"name": datastore_name}) else: raise SaltCloudSystemExit( "The vm_migrate function requires either a 'datastore_id' or a " "'datastore_name' to be provided." ) if host_id: if host_name: log.warning( "Both the 'host_id' and 'host_name' arguments were provided. " "'host_id' will take precedence." ) elif host_name: host_id = get_host_id(kwargs={"name": host_name}) else: raise SaltCloudSystemExit( "The vm_migrate function requires either a 'host_id' " "or a 'host_name' to be provided." ) server, user, password = _get_xml_rpc() auth = ":".join([user, password]) vm_id = int(get_vm_id(kwargs={"name": name})) response = server.one.vm.migrate( auth, vm_id, int(host_id), salt.utils.data.is_true(live_migration), salt.utils.data.is_true(capacity_maintained), int(datastore_id), ) data = { "action": "vm.migrate", "migrated": response[0], "vm_id": response[1], "error_code": response[2], } return data def vm_monitoring(name, call=None): """ Returns the monitoring records for a given virtual machine. A VM name must be supplied. The monitoring information returned is a list of VM elements. Each VM element contains the complete dictionary of the VM with the updated information returned by the poll action. .. versionadded:: 2016.3.0 name The name of the VM for which to gather monitoring records. CLI Example: .. code-block:: bash salt-cloud -a vm_monitoring my-vm """ if call != "action": raise SaltCloudSystemExit( "The vm_monitoring action must be called with -a or --action." ) server, user, password = _get_xml_rpc() auth = ":".join([user, password]) vm_id = int(get_vm_id(kwargs={"name": name})) response = server.one.vm.monitoring(auth, vm_id) if response[0] is False: log.error( "There was an error retrieving the specified VM's monitoring information." ) return {} else: info = {} for vm_ in _get_xml(response[1]): info[vm_.find("ID").text] = _xml_to_dict(vm_) return info def vm_resize(name, kwargs=None, call=None): """ Changes the capacity of the virtual machine. .. versionadded:: 2016.3.0 name The name of the VM to resize. path The path to a file containing new capacity elements CPU, VCPU, MEMORY. If one of them is not present, or its value is 0, the VM will not be re-sized. Syntax within the file can be the usual attribute=value or XML. Can be used instead of ``data``. data Contains the new capacity elements CPU, VCPU, and MEMORY. If one of them is not present, or its value is 0, the VM will not be re-sized. Can be used instead of ``path``. capacity_maintained True to enforce the Host capacity is not over-committed. This parameter is only acknowledged for users in the ``oneadmin`` group. Host capacity will be always enforced for regular users. CLI Example: .. code-block:: bash salt-cloud -a vm_resize my-vm path=/path/to/capacity_template.txt salt-cloud -a vm_resize my-vm path=/path/to/capacity_template.txt capacity_maintained=False salt-cloud -a vm_resize my-vm data="CPU=1 VCPU=1 MEMORY=1024" """ if call != "action": raise SaltCloudSystemExit( "The vm_resize action must be called with -a or --action." ) if kwargs is None: kwargs = {} path = kwargs.get("path", None) data = kwargs.get("data", None) capacity_maintained = kwargs.get("capacity_maintained", True) if data: if path: log.warning( "Both the 'data' and 'path' arguments were provided. " "'data' will take precedence." ) elif path: with salt.utils.files.fopen(path, mode="r") as rfh: data = rfh.read() else: raise SaltCloudSystemExit( "The vm_resize function requires either 'data' or a file 'path' " "to be provided." ) server, user, password = _get_xml_rpc() auth = ":".join([user, password]) vm_id = int(get_vm_id(kwargs={"name": name})) response = server.one.vm.resize( auth, vm_id, data, salt.utils.data.is_true(capacity_maintained) ) ret = { "action": "vm.resize", "resized": response[0], "vm_id": response[1], "error_code": response[2], } return ret def vm_snapshot_create(vm_name, kwargs=None, call=None): """ Creates a new virtual machine snapshot from the provided VM. .. versionadded:: 2016.3.0 vm_name The name of the VM from which to create the snapshot. snapshot_name The name of the snapshot to be created. CLI Example: .. code-block:: bash salt-cloud -a vm_snapshot_create my-vm snapshot_name=my-new-snapshot """ if call != "action": raise SaltCloudSystemExit( "The vm_snapshot_create action must be called with -a or --action." ) if kwargs is None: kwargs = {} snapshot_name = kwargs.get("snapshot_name", None) if snapshot_name is None: raise SaltCloudSystemExit( "The vm_snapshot_create function requires a 'snapshot_name' to be provided." ) server, user, password = _get_xml_rpc() auth = ":".join([user, password]) vm_id = int(get_vm_id(kwargs={"name": vm_name})) response = server.one.vm.snapshotcreate(auth, vm_id, snapshot_name) data = { "action": "vm.snapshotcreate", "snapshot_created": response[0], "snapshot_id": response[1], "error_code": response[2], } return data def vm_snapshot_delete(vm_name, kwargs=None, call=None): """ Deletes a virtual machine snapshot from the provided VM. .. versionadded:: 2016.3.0 vm_name The name of the VM from which to delete the snapshot. snapshot_id The ID of the snapshot to be deleted. CLI Example: .. code-block:: bash salt-cloud -a vm_snapshot_delete my-vm snapshot_id=8 """ if call != "action": raise SaltCloudSystemExit( "The vm_snapshot_delete action must be called with -a or --action." ) if kwargs is None: kwargs = {} snapshot_id = kwargs.get("snapshot_id", None) if snapshot_id is None: raise SaltCloudSystemExit( "The vm_snapshot_delete function requires a 'snapshot_id' to be provided." ) server, user, password = _get_xml_rpc() auth = ":".join([user, password]) vm_id = int(get_vm_id(kwargs={"name": vm_name})) response = server.one.vm.snapshotdelete(auth, vm_id, int(snapshot_id)) data = { "action": "vm.snapshotdelete", "snapshot_deleted": response[0], "vm_id": response[1], "error_code": response[2], } return data def vm_snapshot_revert(vm_name, kwargs=None, call=None): """ Reverts a virtual machine to a snapshot .. versionadded:: 2016.3.0 vm_name The name of the VM to revert. snapshot_id The snapshot ID. CLI Example: .. code-block:: bash salt-cloud -a vm_snapshot_revert my-vm snapshot_id=42 """ if call != "action": raise SaltCloudSystemExit( "The vm_snapshot_revert action must be called with -a or --action." ) if kwargs is None: kwargs = {} snapshot_id = kwargs.get("snapshot_id", None) if snapshot_id is None: raise SaltCloudSystemExit( "The vm_snapshot_revert function requires a 'snapshot_id' to be provided." ) server, user, password = _get_xml_rpc() auth = ":".join([user, password]) vm_id = int(get_vm_id(kwargs={"name": vm_name})) response = server.one.vm.snapshotrevert(auth, vm_id, int(snapshot_id)) data = { "action": "vm.snapshotrevert", "snapshot_reverted": response[0], "vm_id": response[1], "error_code": response[2], } return data def vm_update(name, kwargs=None, call=None): """ Replaces the user template contents. .. versionadded:: 2016.3.0 name The name of the VM to update. path The path to a file containing new user template contents. Syntax within the file can be the usual attribute=value or XML. Can be used instead of ``data``. data Contains the new user template contents. Syntax can be the usual attribute=value or XML. Can be used instead of ``path``. update_type There are two ways to update a VM: ``replace`` the whole template or ``merge`` the new template with the existing one. CLI Example: .. code-block:: bash salt-cloud -a vm_update my-vm path=/path/to/user_template_file.txt update_type='replace' """ if call != "action": raise SaltCloudSystemExit( "The vm_update action must be called with -a or --action." ) if kwargs is None: kwargs = {} path = kwargs.get("path", None) data = kwargs.get("data", None) update_type = kwargs.get("update_type", None) update_args = ["replace", "merge"] if update_type is None: raise SaltCloudSystemExit( "The vm_update function requires an 'update_type' to be provided." ) if update_type == update_args[0]: update_number = 0 elif update_type == update_args[1]: update_number = 1 else: raise SaltCloudSystemExit( "The update_type argument must be either {} or {}.".format( update_args[0], update_args[1] ) ) if data: if path: log.warning( "Both the 'data' and 'path' arguments were provided. " "'data' will take precedence." ) elif path: with salt.utils.files.fopen(path, mode="r") as rfh: data = rfh.read() else: raise SaltCloudSystemExit( "The vm_update function requires either 'data' or a file 'path' " "to be provided." ) server, user, password = _get_xml_rpc() auth = ":".join([user, password]) vm_id = int(get_vm_id(kwargs={"name": name})) response = server.one.vm.update(auth, vm_id, data, int(update_number)) ret = { "action": "vm.update", "updated": response[0], "resource_id": response[1], "error_code": response[2], } return ret def vn_add_ar(call=None, kwargs=None): """ Adds address ranges to a given virtual network. .. versionadded:: 2016.3.0 vn_id The ID of the virtual network to add the address range. Can be used instead of ``vn_name``. vn_name The name of the virtual network to add the address range. Can be used instead of ``vn_id``. path The path to a file containing the template of the address range to add. Syntax within the file can be the usual attribute=value or XML. Can be used instead of ``data``. data Contains the template of the address range to add. Syntax can be the usual attribute=value or XML. Can be used instead of ``path``. CLI Example: .. code-block:: bash salt-cloud -f vn_add_ar opennebula vn_id=3 path=/path/to/address_range.txt salt-cloud -f vn_add_ar opennebula vn_name=my-vn \\ data="AR=[TYPE=IP4, IP=192.168.0.5, SIZE=10]" """ if call != "function": raise SaltCloudSystemExit( "The vn_add_ar function must be called with -f or --function." ) if kwargs is None: kwargs = {} vn_id = kwargs.get("vn_id", None) vn_name = kwargs.get("vn_name", None) path = kwargs.get("path", None) data = kwargs.get("data", None) if vn_id: if vn_name: log.warning( "Both the 'vn_id' and 'vn_name' arguments were provided. " "'vn_id' will take precedence." ) elif vn_name: vn_id = get_vn_id(kwargs={"name": vn_name}) else: raise SaltCloudSystemExit( "The vn_add_ar function requires a 'vn_id' and a 'vn_name' to be provided." ) if data: if path: log.warning( "Both the 'data' and 'path' arguments were provided. " "'data' will take precedence." ) elif path: with salt.utils.files.fopen(path, mode="r") as rfh: data = rfh.read() else: raise SaltCloudSystemExit( "The vn_add_ar function requires either 'data' or a file 'path' " "to be provided." ) server, user, password = _get_xml_rpc() auth = ":".join([user, password]) response = server.one.vn.add_ar(auth, int(vn_id), data) ret = { "action": "vn.add_ar", "address_range_added": response[0], "resource_id": response[1], "error_code": response[2], } return ret def vn_allocate(call=None, kwargs=None): """ Allocates a new virtual network in OpenNebula. .. versionadded:: 2016.3.0 path The path to a file containing the template of the virtual network to allocate. Syntax within the file can be the usual attribute=value or XML. Can be used instead of ``data``. data Contains the template of the virtual network to allocate. Syntax can be the usual attribute=value or XML. Can be used instead of ``path``. cluster_id The ID of the cluster for which to add the new virtual network. Can be used instead of ``cluster_name``. If neither ``cluster_id`` nor ``cluster_name`` are provided, the virtual network won’t be added to any cluster. cluster_name The name of the cluster for which to add the new virtual network. Can be used instead of ``cluster_id``. If neither ``cluster_name`` nor ``cluster_id`` are provided, the virtual network won't be added to any cluster. CLI Example: .. code-block:: bash salt-cloud -f vn_allocate opennebula path=/path/to/vn_file.txt """ if call != "function": raise SaltCloudSystemExit( "The vn_allocate function must be called with -f or --function." ) if kwargs is None: kwargs = {} cluster_id = kwargs.get("cluster_id", None) cluster_name = kwargs.get("cluster_name", None) path = kwargs.get("path", None) data = kwargs.get("data", None) if data: if path: log.warning( "Both the 'data' and 'path' arguments were provided. " "'data' will take precedence." ) elif path: with salt.utils.files.fopen(path, mode="r") as rfh: data = rfh.read() else: raise SaltCloudSystemExit( "The vn_allocate function requires either 'data' or a file 'path' " "to be provided." ) if cluster_id: if cluster_name: log.warning( "Both the 'cluster_id' and 'cluster_name' arguments were provided. " "'cluster_id' will take precedence." ) elif cluster_name: cluster_id = get_cluster_id(kwargs={"name": cluster_name}) else: cluster_id = "-1" server, user, password = _get_xml_rpc() auth = ":".join([user, password]) response = server.one.vn.allocate(auth, data, int(cluster_id)) ret = { "action": "vn.allocate", "allocated": response[0], "vn_id": response[1], "error_code": response[2], } return ret def vn_delete(call=None, kwargs=None): """ Deletes the given virtual network from OpenNebula. Either a name or a vn_id must be supplied. .. versionadded:: 2016.3.0 name The name of the virtual network to delete. Can be used instead of ``vn_id``. vn_id The ID of the virtual network to delete. Can be used instead of ``name``. CLI Example: .. code-block:: bash salt-cloud -f vn_delete opennebula name=my-virtual-network salt-cloud --function vn_delete opennebula vn_id=3 """ if call != "function": raise SaltCloudSystemExit( "The vn_delete function must be called with -f or --function." ) if kwargs is None: kwargs = {} name = kwargs.get("name", None) vn_id = kwargs.get("vn_id", None) if vn_id: if name: log.warning( "Both the 'vn_id' and 'name' arguments were provided. " "'vn_id' will take precedence." ) elif name: vn_id = get_vn_id(kwargs={"name": name}) else: raise SaltCloudSystemExit( "The vn_delete function requires a 'name' or a 'vn_id' to be provided." ) server, user, password = _get_xml_rpc() auth = ":".join([user, password]) response = server.one.vn.delete(auth, int(vn_id)) data = { "action": "vn.delete", "deleted": response[0], "vn_id": response[1], "error_code": response[2], } return data def vn_free_ar(call=None, kwargs=None): """ Frees a reserved address range from a virtual network. .. versionadded:: 2016.3.0 vn_id The ID of the virtual network from which to free an address range. Can be used instead of ``vn_name``. vn_name The name of the virtual network from which to free an address range. Can be used instead of ``vn_id``. ar_id The ID of the address range to free. CLI Example: .. code-block:: bash salt-cloud -f vn_free_ar opennebula vn_id=3 ar_id=1 salt-cloud -f vn_free_ar opennebula vn_name=my-vn ar_id=1 """ if call != "function": raise SaltCloudSystemExit( "The vn_free_ar function must be called with -f or --function." ) if kwargs is None: kwargs = {} vn_id = kwargs.get("vn_id", None) vn_name = kwargs.get("vn_name", None) ar_id = kwargs.get("ar_id", None) if ar_id is None: raise SaltCloudSystemExit( "The vn_free_ar function requires an 'rn_id' to be provided." ) if vn_id: if vn_name: log.warning( "Both the 'vn_id' and 'vn_name' arguments were provided. " "'vn_id' will take precedence." ) elif vn_name: vn_id = get_vn_id(kwargs={"name": vn_name}) else: raise SaltCloudSystemExit( "The vn_free_ar function requires a 'vn_id' or a 'vn_name' to be provided." ) server, user, password = _get_xml_rpc() auth = ":".join([user, password]) response = server.one.vn.free_ar(auth, int(vn_id), int(ar_id)) data = { "action": "vn.free_ar", "ar_freed": response[0], "resource_id": response[1], "error_code": response[2], } return data def vn_hold(call=None, kwargs=None): """ Holds a virtual network lease as used. .. versionadded:: 2016.3.0 vn_id The ID of the virtual network from which to hold the lease. Can be used instead of ``vn_name``. vn_name The name of the virtual network from which to hold the lease. Can be used instead of ``vn_id``. path The path to a file defining the template of the lease to hold. Syntax within the file can be the usual attribute=value or XML. Can be used instead of ``data``. data Contains the template of the lease to hold. Syntax can be the usual attribute=value or XML. Can be used instead of ``path``. CLI Example: .. code-block:: bash salt-cloud -f vn_hold opennebula vn_id=3 path=/path/to/vn_hold_file.txt salt-cloud -f vn_hold opennebula vn_name=my-vn data="LEASES=[IP=192.168.0.5]" """ if call != "function": raise SaltCloudSystemExit( "The vn_hold function must be called with -f or --function." ) if kwargs is None: kwargs = {} vn_id = kwargs.get("vn_id", None) vn_name = kwargs.get("vn_name", None) path = kwargs.get("path", None) data = kwargs.get("data", None) if vn_id: if vn_name: log.warning( "Both the 'vn_id' and 'vn_name' arguments were provided. " "'vn_id' will take precedence." ) elif vn_name: vn_id = get_vn_id(kwargs={"name": vn_name}) else: raise SaltCloudSystemExit( "The vn_hold function requires a 'vn_id' or a 'vn_name' to be provided." ) if data: if path: log.warning( "Both the 'data' and 'path' arguments were provided. " "'data' will take precedence." ) elif path: with salt.utils.files.fopen(path, mode="r") as rfh: data = rfh.read() else: raise SaltCloudSystemExit( "The vn_hold function requires either 'data' or a 'path' to be provided." ) server, user, password = _get_xml_rpc() auth = ":".join([user, password]) response = server.one.vn.hold(auth, int(vn_id), data) ret = { "action": "vn.hold", "held": response[0], "resource_id": response[1], "error_code": response[2], } return ret def vn_info(call=None, kwargs=None): """ Retrieves information for the virtual network. .. versionadded:: 2016.3.0 name The name of the virtual network for which to gather information. Can be used instead of ``vn_id``. vn_id The ID of the virtual network for which to gather information. Can be used instead of ``name``. CLI Example: .. code-block:: bash salt-cloud -f vn_info opennebula vn_id=3 salt-cloud --function vn_info opennebula name=public """ if call != "function": raise SaltCloudSystemExit( "The vn_info function must be called with -f or --function." ) if kwargs is None: kwargs = {} name = kwargs.get("name", None) vn_id = kwargs.get("vn_id", None) if vn_id: if name: log.warning( "Both the 'vn_id' and 'name' arguments were provided. " "'vn_id' will take precedence." ) elif name: vn_id = get_vn_id(kwargs={"name": name}) else: raise SaltCloudSystemExit( "The vn_info function requires either a 'name' or a 'vn_id' to be provided." ) server, user, password = _get_xml_rpc() auth = ":".join([user, password]) response = server.one.vn.info(auth, int(vn_id)) if response[0] is False: return response[1] else: info = {} tree = _get_xml(response[1]) info[tree.find("NAME").text] = _xml_to_dict(tree) return info def vn_release(call=None, kwargs=None): """ Releases a virtual network lease that was previously on hold. .. versionadded:: 2016.3.0 vn_id The ID of the virtual network from which to release the lease. Can be used instead of ``vn_name``. vn_name The name of the virtual network from which to release the lease. Can be used instead of ``vn_id``. path The path to a file defining the template of the lease to release. Syntax within the file can be the usual attribute=value or XML. Can be used instead of ``data``. data Contains the template defining the lease to release. Syntax can be the usual attribute=value or XML. Can be used instead of ``path``. CLI Example: .. code-block:: bash salt-cloud -f vn_release opennebula vn_id=3 path=/path/to/vn_release_file.txt salt-cloud =f vn_release opennebula vn_name=my-vn data="LEASES=[IP=192.168.0.5]" """ if call != "function": raise SaltCloudSystemExit( "The vn_reserve function must be called with -f or --function." ) if kwargs is None: kwargs = {} vn_id = kwargs.get("vn_id", None) vn_name = kwargs.get("vn_name", None) path = kwargs.get("path", None) data = kwargs.get("data", None) if vn_id: if vn_name: log.warning( "Both the 'vn_id' and 'vn_name' arguments were provided. " "'vn_id' will take precedence." ) elif vn_name: vn_id = get_vn_id(kwargs={"name": vn_name}) else: raise SaltCloudSystemExit( "The vn_release function requires a 'vn_id' or a 'vn_name' to be provided." ) if data: if path: log.warning( "Both the 'data' and 'path' arguments were provided. " "'data' will take precedence." ) elif path: with salt.utils.files.fopen(path, mode="r") as rfh: data = rfh.read() else: raise SaltCloudSystemExit( "The vn_release function requires either 'data' or a 'path' to be provided." ) server, user, password = _get_xml_rpc() auth = ":".join([user, password]) response = server.one.vn.release(auth, int(vn_id), data) ret = { "action": "vn.release", "released": response[0], "resource_id": response[1], "error_code": response[2], } return ret def vn_reserve(call=None, kwargs=None): """ Reserve network addresses. .. versionadded:: 2016.3.0 vn_id The ID of the virtual network from which to reserve addresses. Can be used instead of vn_name. vn_name The name of the virtual network from which to reserve addresses. Can be used instead of vn_id. path The path to a file defining the template of the address reservation. Syntax within the file can be the usual attribute=value or XML. Can be used instead of ``data``. data Contains the template defining the address reservation. Syntax can be the usual attribute=value or XML. Data provided must be wrapped in double quotes. Can be used instead of ``path``. CLI Example: .. code-block:: bash salt-cloud -f vn_reserve opennebula vn_id=3 path=/path/to/vn_reserve_file.txt salt-cloud -f vn_reserve opennebula vn_name=my-vn data="SIZE=10 AR_ID=8 NETWORK_ID=1" """ if call != "function": raise SaltCloudSystemExit( "The vn_reserve function must be called with -f or --function." ) if kwargs is None: kwargs = {} vn_id = kwargs.get("vn_id", None) vn_name = kwargs.get("vn_name", None) path = kwargs.get("path", None) data = kwargs.get("data", None) if vn_id: if vn_name: log.warning( "Both the 'vn_id' and 'vn_name' arguments were provided. " "'vn_id' will take precedence." ) elif vn_name: vn_id = get_vn_id(kwargs={"name": vn_name}) else: raise SaltCloudSystemExit( "The vn_reserve function requires a 'vn_id' or a 'vn_name' to be provided." ) if data: if path: log.warning( "Both the 'data' and 'path' arguments were provided. " "'data' will take precedence." ) elif path: with salt.utils.files.fopen(path, mode="r") as rfh: data = rfh.read() else: raise SaltCloudSystemExit( "The vn_reserve function requires a 'path' to be provided." ) server, user, password = _get_xml_rpc() auth = ":".join([user, password]) response = server.one.vn.reserve(auth, int(vn_id), data) ret = { "action": "vn.reserve", "reserved": response[0], "resource_id": response[1], "error_code": response[2], } return ret # Helper Functions def _get_node(name): """ Helper function that returns all information about a named node. name The name of the node for which to get information. """ attempts = 10 while attempts >= 0: try: return list_nodes_full()[name] except KeyError: attempts -= 1 log.debug( "Failed to get the data for node '%s'. Remaining attempts: %s", name, attempts, ) # Just a little delay between attempts... time.sleep(0.5) return {} def _get_xml(xml_str): """ Intrepret the data coming from opennebula and raise if it's not XML. """ try: xml_data = etree.XML(xml_str) # XMLSyntaxError seems to be only available from lxml, but that is the xml # library loaded by this module except etree.XMLSyntaxError as err: # opennebula returned invalid XML, which could be an error message, so # log it raise SaltCloudSystemExit(f"opennebula returned: {xml_str}") return xml_data def _get_xml_rpc(): """ Uses the OpenNebula cloud provider configurations to connect to the OpenNebula API. Returns the server connection created as well as the user and password values from the cloud provider config file used to make the connection. """ vm_ = get_configured_provider() xml_rpc = config.get_cloud_config_value( "xml_rpc", vm_, __opts__, search_global=False ) user = config.get_cloud_config_value("user", vm_, __opts__, search_global=False) password = config.get_cloud_config_value( "password", vm_, __opts__, search_global=False ) server = xmlrpc.client.ServerProxy(xml_rpc) return server, user, password def _list_nodes(full=False): """ Helper function for the list_* query functions - Constructs the appropriate dictionaries to return from the API query. full If performing a full query, such as in list_nodes_full, change this parameter to ``True``. """ server, user, password = _get_xml_rpc() auth = ":".join([user, password]) vm_pool = server.one.vmpool.info(auth, -2, -1, -1, -1)[1] vms = {} for vm in _get_xml(vm_pool): name = vm.find("NAME").text vms[name] = {} cpu_size = vm.find("TEMPLATE").find("CPU").text memory_size = vm.find("TEMPLATE").find("MEMORY").text private_ips = [] for nic in vm.find("TEMPLATE").findall("NIC"): try: private_ips.append(nic.find("IP").text) except Exception: # pylint: disable=broad-except pass vms[name]["id"] = vm.find("ID").text if "TEMPLATE_ID" in vm.find("TEMPLATE"): vms[name]["image"] = vm.find("TEMPLATE").find("TEMPLATE_ID").text vms[name]["name"] = name vms[name]["size"] = {"cpu": cpu_size, "memory": memory_size} vms[name]["state"] = vm.find("STATE").text vms[name]["private_ips"] = private_ips vms[name]["public_ips"] = [] if full: vms[vm.find("NAME").text] = _xml_to_dict(vm) return vms def _xml_to_dict(xml): """ Helper function to covert xml into a data dictionary. xml The xml data to convert. """ dicts = {} for item in xml: key = item.tag.lower() idx = 1 while key in dicts: key += str(idx) idx += 1 if item.text is None: dicts[key] = _xml_to_dict(item) else: dicts[key] = item.text return dicts