From b9c565f191590a7fe6841f8844ee653f1980e1f6 Mon Sep 17 00:00:00 2001 From: Zhiwei Liang Date: Mon, 10 Jul 2023 15:31:32 -0400 Subject: [PATCH] Remove Linode API v3 usage --- salt/cloud/clouds/linode.py | 1300 +++-------------------------------- 1 file changed, 81 insertions(+), 1219 deletions(-) diff --git a/salt/cloud/clouds/linode.py b/salt/cloud/clouds/linode.py index 72c985f148e..46302983163 100644 --- a/salt/cloud/clouds/linode.py +++ b/salt/cloud/clouds/linode.py @@ -4,8 +4,6 @@ The Linode Cloud Module The Linode cloud module is used to interact with the Linode Cloud. -You can target a specific version of the Linode API with the ``api_version`` parameter. The default is ``v3``. - Provider -------- @@ -13,14 +11,13 @@ The following provider parameters are supported: - **apikey**: (required) The key to use to authenticate with the Linode API. - **password**: (required) The default password to set on new VMs. Must be 8 characters with at least one lowercase, uppercase, and numeric. -- **api_version**: (optional) The version of the Linode API to interact with. Defaults to ``v3``. - **poll_interval**: (optional) The rate of time in milliseconds to poll the Linode API for changes. Defaults to ``500``. - **ratelimit_sleep**: (optional) The time in seconds to wait before retrying after a ratelimit has been enforced. Defaults to ``0``. .. note:: - APIv3 usage is deprecated and will be removed in a future release in favor of APIv4. To move to APIv4 now, - set the ``api_version`` parameter in your provider configuration to ``v4``. See the full migration guide + APIv3 usage has been removed in favor of APIv4. To move to APIv4 now, + See the full migration guide here https://docs.saltproject.io/en/latest/topics/cloud/linode.html#migrating-to-apiv4. Set up the provider configuration at ``/etc/salt/cloud.providers`` or ``/etc/salt/cloud.providers.d/linode.conf``: @@ -29,34 +26,23 @@ Set up the provider configuration at ``/etc/salt/cloud.providers`` or ``/etc/sal my-linode-provider: driver: linode - api_version: v4 apikey: f4ZsmwtB1c7f85Jdu43RgXVDFlNjuJaeIYV8QMftTqKScEB2vSosFSr... - password: F00barbaz - -For use with APIv3 (deprecated): - -.. code-block:: yaml - - my-linode-provider-v3: - driver: linode - apikey: f4ZsmwtB1c7f85Jdu43RgXVDFlNjuJaeIYV8QMftTqKScEB2vSosFSr... - password: F00barbaz + password: F00barbazverylongp@ssword Profile ------- The following profile parameters are supported: -- **size**: (required) The size of the VM. This should be a Linode instance type ID (i.e. ``g6-standard-2``). For APIv3, this would be a plan ID (i.e. ``Linode 2GB``). Run ``salt-cloud -f avail_sizes my-linode-provider`` for options. -- **location**: (required) The location of the VM. This should be a Linode region (e.g. ``us-east``). For APIv3, this would be a datacenter location (i.e. ``Newark, NJ, USA``). Run ``salt-cloud -f avail_locations my-linode-provider`` for options. -- **image**: (required) The image to deploy the boot disk from. This should be an image ID (e.g. ``linode/ubuntu16.04``); official images start with ``linode/``. For APIv3, this would be an image label (i.e. Ubuntu 16.04). Run ``salt-cloud -f avail_images my-linode-provider`` for more options. +- **size**: (required) The size of the VM. This should be a Linode instance type ID (i.e. ``g6-standard-2``). Run ``salt-cloud -f avail_sizes my-linode-provider`` for options. +- **location**: (required) The location of the VM. This should be a Linode region (e.g. ``us-east``). Run ``salt-cloud -f avail_locations my-linode-provider`` for options. +- **image**: (required) The image to deploy the boot disk from. This should be an image ID (e.g. ``linode/ubuntu22.04``); official images start with ``linode/``. Run ``salt-cloud -f avail_images my-linode-provider`` for more options. - **password**: (\*required) The default password for the VM. Must be provided at the profile or provider level. - **assign_private_ip**: (optional) Whether or not to assign a private key to the VM. Defaults to ``False``. - **ssh_interface**: (optional) The interface with which to connect over SSH. Valid options are ``private_ips`` or ``public_ips``. Defaults to ``public_ips``. - **ssh_pubkey**: (optional) The public key to authorize for SSH with the VM. - **swap**: (optional) The amount of disk space to allocate for the swap partition. Defaults to ``256``. - **clonefrom**: (optional) The name of the Linode to clone from. -- **disk_size**: (deprecated, optional) The amount of disk space to allocate for the OS disk. This has no effect with APIv4; the size of the boot disk will be the remainder of disk space after the swap parition is allocated. Set up a profile configuration in ``/etc/salt/cloud.profiles.d/``: @@ -81,46 +67,31 @@ Set up a profile configuration in ``/etc/salt/cloud.profiles.d/``: ssh_pubkey: ssh-rsa AAAAB3NzaC1yc2EAAAADAQAB... swap_size: 512 - my-linode-profile-v3: - # a legacy configuration - provider: my-linode-provider-v3 - size: Nanode 1GB - image: Alpine 3.12 - location: Fremont, CA, USA - Migrating to APIv4 ------------------ -In order to target APIv4, ensure your provider configuration has ``api_version`` set to ``v4``. - -You will also need to generate a new token for your account. See https://www.linode.com/docs/platform/api/getting-started-with-the-linode-api/#create-an-api-token +You will need to generate a new token for your account. See https://www.linode.com/docs/platform/api/getting-started-with-the-linode-api/#create-an-api-token There are a few changes to note: - There has been a general move from label references to ID references. The profile configuration parameters ``location``, ``size``, and ``image`` have moved from being label based references to IDs. See the profile section for more information. In addition to these inputs being changed, ``avail_sizes``, ``avail_locations``, and ``avail_images`` now output options sorted by ID instead of label. - The ``disk_size`` profile configuration parameter has been deprecated and will not be taken into account when creating new VMs while targeting APIv4. -:maintainer: Charles Kenney -:maintainer: Phillip Campbell +:maintainer: Linode Developer Tools and Experience Team :depends: requests """ -import abc import datetime import json import logging import pprint import re import time +from abc import ABC, abstractmethod from pathlib import Path import salt.config as config from salt._compat import ipaddress -from salt.exceptions import ( - SaltCloudConfigError, - SaltCloudException, - SaltCloudNotFound, - SaltCloudSystemExit, -) +from salt.exceptions import SaltCloudException, SaltCloudNotFound, SaltCloudSystemExit try: import requests @@ -132,22 +103,9 @@ except ImportError: # Get logging started log = logging.getLogger(__name__) -HAS_WARNED_FOR_API_V3 = False - # The epoch of the last time a query was made LASTCALL = int(time.mktime(datetime.datetime.now().timetuple())) -# Human-readable status fields for APIv3 (documentation: https://www.linode.com/api/linode/linode.list) -LINODE_STATUS = { - "boot_failed": {"code": -2, "descr": "Boot Failed (not in use)"}, - "beeing_created": {"code": -1, "descr": "Being Created"}, - "brand_new": {"code": 0, "descr": "Brand New"}, - "running": {"code": 1, "descr": "Running"}, - "poweroff": {"code": 2, "descr": "Powered Off"}, - "shutdown": {"code": 3, "descr": "Shutting Down (not in use)"}, - "save_to_disk": {"code": 4, "descr": "Saved to Disk (not in use)"}, -} - __virtualname__ = "linode" @@ -191,32 +149,6 @@ def _get_dependencies(): return config.check_driver_dependencies(__virtualname__, deps) -def _get_api_version(): - """ - Return the configured Linode API version. - """ - return config.get_cloud_config_value( - "api_version", - get_configured_provider(), - __opts__, - search_global=False, - default="v3", - ) - - -def _is_api_v3(): - """ - Return whether the configured Linode API version is ``v3``. - """ - return _get_api_version() == "v3" - - -def _get_cloud_interface(): - if _is_api_v3(): - return LinodeAPIv3() - return LinodeAPIv4() - - def _get_api_key(): """ Returned the configured Linode API key. @@ -277,15 +209,6 @@ def _get_password(vm_): ) -def _get_root_disk_size(vm_): - """ - Return the specified size of the data partition. - """ - return config.get_cloud_config_value( - "disk_size", vm_, __opts__, search_global=False - ) - - def _get_private_ip(vm_): """ Return True if a private ip address is requested @@ -339,7 +262,7 @@ def _get_ssh_keys(vm_): key_files = _get_ssh_key_files(vm_) for file in map(lambda file: Path(file).resolve(), key_files): if not (file.exists() or file.is_file()): - raise SaltCloudSystemExit("Invalid SSH key file: {}".format(str(file))) + raise SaltCloudSystemExit(f"Invalid SSH key file: {str(file)}") ssh_keys.add(file.read_text()) return list(ssh_keys) @@ -385,102 +308,83 @@ def _validate_name(name): return ret -def _warn_for_api_v3(): - global HAS_WARNED_FOR_API_V3 - - if not HAS_WARNED_FOR_API_V3: - log.warning( - "Linode APIv3 has been deprecated and support will be removed " - "in future releases. Please plan to upgrade to APIv4. For more " - "information, see" - " https://docs.saltproject.io/en/latest/topics/cloud/linode.html#migrating-to-apiv4." - ) - HAS_WARNED_FOR_API_V3 = True - - -class LinodeAPI: - @abc.abstractmethod +class LinodeAPI(ABC): + @abstractmethod def avail_images(self): """avail_images implementation""" - @abc.abstractmethod + @abstractmethod def avail_locations(self): """avail_locations implementation""" - @abc.abstractmethod + @abstractmethod def avail_sizes(self): """avail_sizes implementation""" - @abc.abstractmethod + @abstractmethod def boot(self, name=None, kwargs=None): """boot implementation""" - @abc.abstractmethod + @abstractmethod def clone(self, kwargs=None): """clone implementation""" - @abc.abstractmethod + @abstractmethod def create_config(self, kwargs=None): """create_config implementation""" - @abc.abstractmethod + @abstractmethod def create(self, vm_): """create implementation""" - @abc.abstractmethod + @abstractmethod def destroy(self, name): """destroy implementation""" - @abc.abstractmethod + @abstractmethod def get_config_id(self, kwargs=None): """get_config_id implementation""" - @abc.abstractmethod + @abstractmethod def list_nodes(self): """list_nodes implementation""" - @abc.abstractmethod + @abstractmethod def list_nodes_full(self): """list_nodes_full implementation""" - @abc.abstractmethod + @abstractmethod def list_nodes_min(self): """list_nodes_min implementation""" - @abc.abstractmethod + @abstractmethod def reboot(self, name): """reboot implementation""" - @abc.abstractmethod + @abstractmethod def show_instance(self, name): """show_instance implementation""" - @abc.abstractmethod + @abstractmethod def show_pricing(self, kwargs=None): """show_pricing implementation""" - @abc.abstractmethod + @abstractmethod def start(self, name): """start implementation""" - @abc.abstractmethod + @abstractmethod def stop(self, name): """stop implementation""" - @abc.abstractmethod + @abstractmethod def _get_linode_by_name(self, name): """_get_linode_by_name implementation""" - @abc.abstractmethod + @abstractmethod def _get_linode_by_id(self, linode_id): """_get_linode_by_id implementation""" - def get_plan_id(self, kwargs=None): - """get_plan_id implementation""" - raise SaltCloudSystemExit( - "The get_plan_id is not supported by this api_version." - ) - def get_linode(self, kwargs=None): name = kwargs.get("name", None) linode_id = kwargs.get("linode_id", None) @@ -503,21 +407,26 @@ class LinodeAPI: class LinodeAPIv4(LinodeAPI): - def _query(self, path=None, method="GET", data=None, headers=None): + @classmethod + def get_api_instance(cls): + if not hasattr(cls, "api_instance"): + cls.api_instance = cls() + return cls.api_instance + + def _query(self, path, method="GET", data=None, headers=None): """ Make a call to the Linode API. """ - api_version = _get_api_version() api_key = _get_api_key() ratelimit_sleep = _get_ratelimit_sleep() if headers is None: headers = {} - headers["Authorization"] = "Bearer {}".format(api_key) + headers["Authorization"] = f"Bearer {api_key}" headers["Content-Type"] = "application/json" headers["User-Agent"] = "salt-cloud-linode" - url = "https://api.linode.com/{}{}".format(api_version, path) + url = f"https://api.linode.com/v4{path}" decode = method != "DELETE" result = None @@ -576,7 +485,7 @@ class LinodeAPIv4(LinodeAPI): # If the response is not valid JSON or the error was not included, propagate the # human readable status representation. raise SaltCloudSystemExit( - "Linode API error occurred: {}".format(err_response.reason) + f"Linode API error occurred: {err_response.reason}" ) if decode: return self._get_response_json(result) @@ -620,8 +529,8 @@ class LinodeAPIv4(LinodeAPI): "Linode {0} is already running.".format(name, linode_id) ) - response = self._query( - "/linode/instances/{}/boot".format(linode_id), + self._query( + f"/linode/instances/{linode_id}/boot", method="POST", data={"config_id": config_id}, ) @@ -634,18 +543,6 @@ class LinodeAPIv4(LinodeAPI): location = kwargs.get("location", None) size = kwargs.get("size", None) - if "datacenter_id" in kwargs: - log.warning( - "The 'datacenter_id' argument has been deprecated and will be " - "removed in future releases. Please use 'location' instead." - ) - - if "plan_id" in kwargs: - log.warning( - "The 'plan_id' argument has been deprecated and will be " - "removed in future releases. Please use 'size' instead." - ) - for item in [linode_id, location, size]: if item is None: raise SaltCloudSystemExit( @@ -654,7 +551,7 @@ class LinodeAPIv4(LinodeAPI): ) return self._query( - "/linode/instances/{}/clone".format(linode_id), + f"/linode/instances/{linode_id}/clone", method="POST", data={"region": location, "type": size}, ) @@ -686,7 +583,7 @@ class LinodeAPIv4(LinodeAPI): } return self._query( - "/linode/instances/{}/configs".format(linode_id), + f"/linode/instances/{linode_id}/configs", method="POST", data={"label": name, "devices": devices}, ) @@ -700,7 +597,7 @@ class LinodeAPIv4(LinodeAPI): __utils__["cloud.fire_event"]( "event", "starting create", - "salt/cloud/{}/creating".format(name), + f"salt/cloud/{name}/creating", args=__utils__["cloud.filter_event"]( "creating", vm_, ["name", "profile", "provider", "driver"] ), @@ -793,7 +690,7 @@ class LinodeAPIv4(LinodeAPI): __utils__["cloud.fire_event"]( "event", "waiting for ssh", - "salt/cloud/{}/waiting_for_ssh".format(name), + f"salt/cloud/{name}/waiting_for_ssh", sock_dir=__opts__["sock_dir"], args={"ip_address": vm_["ssh_host"]}, transport=__opts__["transport"], @@ -808,7 +705,7 @@ class LinodeAPIv4(LinodeAPI): __utils__["cloud.fire_event"]( "event", "created instance", - "salt/cloud/{}/created".format(name), + f"salt/cloud/{name}/created", args=__utils__["cloud.filter_event"]( "created", vm_, ["name", "profile", "provider", "driver"] ), @@ -822,7 +719,7 @@ class LinodeAPIv4(LinodeAPI): __utils__["cloud.fire_event"]( "event", "destroyed instance", - "salt/cloud/{}/destroyed".format(name), + f"salt/cloud/{name}/destroyed", args={"name": name}, sock_dir=__opts__["sock_dir"], transport=__opts__["transport"], @@ -836,7 +733,7 @@ class LinodeAPIv4(LinodeAPI): instance = self._get_linode_by_name(name) linode_id = instance.get("id", None) - self._query("/linode/instances/{}".format(linode_id), method="DELETE") + self._query(f"/linode/instances/{linode_id}", method="DELETE") def get_config_id(self, kwargs=None): name = kwargs.get("name", None) @@ -851,7 +748,7 @@ class LinodeAPIv4(LinodeAPI): if linode_id is None: linode_id = self.get_linode(kwargs=kwargs).get("id", None) - response = self._query("/linode/instances/{}/configs".format(linode_id)) + response = self._query(f"/linode/instances/{linode_id}/configs") configs = response.get("data", []) return {"config_id": configs[0]["id"]} @@ -877,7 +774,7 @@ class LinodeAPIv4(LinodeAPI): instance = self._get_linode_by_name(name) linode_id = instance.get("id", None) - self._query("/linode/instances/{}/reboot".format(linode_id), method="POST") + self._query(f"/linode/instances/{linode_id}/reboot", method="POST") return self._wait_for_linode_status(linode_id, "running") def show_instance(self, name): @@ -937,7 +834,7 @@ class LinodeAPIv4(LinodeAPI): "msg": "Machine already running", } - self._query("/linode/instances/{}/boot".format(linode_id), method="POST") + self._query(f"/linode/instances/{linode_id}/boot", method="POST") self._wait_for_linode_status(linode_id, "running") return { @@ -958,13 +855,13 @@ class LinodeAPIv4(LinodeAPI): "msg": "Machine already stopped", } - self._query("/linode/instances/{}/shutdown".format(linode_id), method="POST") + self._query(f"/linode/instances/{linode_id}/shutdown", method="POST") self._wait_for_linode_status(linode_id, "offline") return {"success": True, "state": "Stopped", "action": "stop"} def _get_linode_by_id(self, linode_id): - return self._query("/linode/instances/{}".format(linode_id)) + return self._query(f"/linode/instances/{linode_id}") def _get_linode_by_name(self, name): result = self._query("/linode/instances") @@ -974,9 +871,7 @@ class LinodeAPIv4(LinodeAPI): if instance["label"] == name: return instance - raise SaltCloudNotFound( - "The specified name, {}, could not be found.".format(name) - ) + raise SaltCloudNotFound(f"The specified name, {name}, could not be found.") def _list_linodes(self, full=False): result = self._query("/linode/instances") @@ -1003,7 +898,7 @@ class LinodeAPIv4(LinodeAPI): return ret def _get_linode_type(self, linode_type): - return self._query("/linode/types/{}".format(linode_type)) + return self._query(f"/linode/types/{linode_type}") def _get_ips(self, linode_id): instance = self._get_linode_by_id(linode_id) @@ -1047,15 +942,13 @@ class LinodeAPIv4(LinodeAPI): time.sleep(poll_interval / 1000) log.info("retrying: polling for %s...", description) else: - raise SaltCloudException( - "timed out: polling for {}".format(description) - ) + raise SaltCloudException(f"timed out: polling for {description}") def _wait_for_entity_status( self, getter, status, entity_name="item", identifier="some", timeout=None ): return self._poll( - "{} (id={}) status to be '{}'".format(entity_name, identifier, status), + f"{entity_name} (id={identifier}) status to be '{status}'", getter, lambda item: item.get("status") == status, timeout=timeout, @@ -1122,8 +1015,8 @@ class LinodeAPIv4(LinodeAPI): return True return self._poll( - "event {} to be '{}'".format(event_id, status), - lambda: self._query("/account/events/{}".format(event_id)), + f"event {event_id} to be '{status}'", + lambda: self._query(f"/account/events/{event_id}"), condition, timeout=timeout, ) @@ -1139,1008 +1032,6 @@ class LinodeAPIv4(LinodeAPI): return json -class LinodeAPIv3(LinodeAPI): - def __init__(self): - _warn_for_api_v3() - - def _query( - self, - action=None, - command=None, - args=None, - method="GET", - header_dict=None, - data=None, - url="https://api.linode.com/", - ): - """ - Make a web call to the Linode API. - """ - global LASTCALL - ratelimit_sleep = _get_ratelimit_sleep() - apikey = _get_api_key() - - if not isinstance(args, dict): - args = {} - - if "api_key" not in args.keys(): - args["api_key"] = apikey - if action and "api_action" not in args.keys(): - args["api_action"] = "{}.{}".format(action, command) - if header_dict is None: - header_dict = {} - if method != "POST": - header_dict["Accept"] = "application/json" - - decode = True - if method == "DELETE": - decode = False - - now = int(time.mktime(datetime.datetime.now().timetuple())) - - if LASTCALL >= now: - time.sleep(ratelimit_sleep) - - result = __utils__["http.query"]( - url, - method, - params=args, - data=data, - header_dict=header_dict, - decode=decode, - decode_type="json", - text=True, - status=True, - hide_fields=["api_key", "rootPass"], - opts=__opts__, - ) - - if "ERRORARRAY" in result["dict"]: - if result["dict"]["ERRORARRAY"]: - error_list = [] - for error in result["dict"]["ERRORARRAY"]: - msg = error["ERRORMESSAGE"] - if msg == "Authentication failed": - raise SaltCloudSystemExit( - "Linode API Key is expired or invalid" - ) - else: - error_list.append(msg) - raise SaltCloudException( - "Linode API reported error(s): {}".format(", ".join(error_list)) - ) - - LASTCALL = int(time.mktime(datetime.datetime.now().timetuple())) - log.debug("Linode Response Status Code: %s", result["status"]) - - return result["dict"] - - def avail_images(self): - response = self._query("avail", "distributions") - - ret = {} - for item in response["DATA"]: - name = item["LABEL"] - ret[name] = item - return ret - - def avail_locations(self): - response = self._query("avail", "datacenters") - - ret = {} - for item in response["DATA"]: - name = item["LOCATION"] - ret[name] = item - return ret - - def avail_sizes(self): - response = self._query("avail", "LinodePlans") - - ret = {} - for item in response["DATA"]: - name = item["LABEL"] - ret[name] = item - return ret - - def boot(self, name=None, kwargs=None): - linode_id = kwargs.get("linode_id", None) - config_id = kwargs.get("config_id", None) - check_running = kwargs.get("check_running", True) - - if config_id is None: - raise SaltCloudSystemExit("The boot function requires a 'config_id'.") - - if linode_id is None: - linode_id = self._get_linode_id_from_name(name) - linode_item = name - else: - linode_item = linode_id - - # Check if Linode is running first - if check_running: - status = get_linode(kwargs={"linode_id": linode_id})["STATUS"] - if status == "1": - raise SaltCloudSystemExit( - "Cannot boot Linode {0}. " - + "Linode {} is already running.".format(linode_item) - ) - - # Boot the VM and get the JobID from Linode - response = self._query( - "linode", "boot", args={"LinodeID": linode_id, "ConfigID": config_id} - )["DATA"] - boot_job_id = response["JobID"] - - if not self._wait_for_job(linode_id, boot_job_id): - log.error("Boot failed for Linode %s.", linode_item) - return False - - return True - - def clone(self, kwargs=None): - linode_id = kwargs.get("linode_id", None) - datacenter_id = kwargs.get("datacenter_id", kwargs.get("location")) - plan_id = kwargs.get("plan_id", kwargs.get("size")) - required_params = [linode_id, datacenter_id, plan_id] - - for item in required_params: - if item is None: - raise SaltCloudSystemExit( - "The clone function requires a 'linode_id', 'datacenter_id', " - "and 'plan_id' to be provided." - ) - - clone_args = { - "LinodeID": linode_id, - "DatacenterID": datacenter_id, - "PlanID": plan_id, - } - - return self._query("linode", "clone", args=clone_args) - - def create(self, vm_): - name = vm_["name"] - - if not _validate_name(name): - return False - - __utils__["cloud.fire_event"]( - "event", - "starting create", - "salt/cloud/{}/creating".format(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", name) - - data = {} - kwargs = {"name": name} - - plan_id = None - size = vm_.get("size") - if size: - kwargs["size"] = size - plan_id = self.get_plan_id(kwargs={"label": size}) - - datacenter_id = None - location = vm_.get("location") - if location: - try: - datacenter_id = self._get_datacenter_id(location) - except KeyError: - # Linode's default datacenter is Dallas, but we still have to set one to - # use the create function from Linode's API. Dallas's datacenter id is 2. - datacenter_id = 2 - - clonefrom_name = vm_.get("clonefrom") - cloning = True if clonefrom_name else False - if cloning: - linode_id = self._get_linode_id_from_name(clonefrom_name) - clone_source = get_linode(kwargs={"linode_id": linode_id}) - - kwargs = { - "clonefrom": clonefrom_name, - "image": "Clone of {}".format(clonefrom_name), - } - - if size is None: - size = clone_source["TOTALRAM"] - kwargs["size"] = size - plan_id = clone_source["PLANID"] - - if location is None: - datacenter_id = clone_source["DATACENTERID"] - - # Create new Linode from cloned Linode - try: - result = clone( - kwargs={ - "linode_id": linode_id, - "datacenter_id": datacenter_id, - "plan_id": plan_id, - } - ) - except Exception as err: # pylint: disable=broad-except - log.error( - "Error cloning '%s' on Linode.\n\n" - "The following exception was thrown by Linode when trying to " - "clone the specified machine:\n%s", - clonefrom_name, - err, - exc_info_on_loglevel=logging.DEBUG, - ) - return False - else: - kwargs["image"] = vm_["image"] - - # Create Linode - try: - result = self._query( - "linode", - "create", - args={"PLANID": plan_id, "DATACENTERID": datacenter_id}, - ) - except Exception as err: # pylint: disable=broad-except - log.error( - "Error creating %s on Linode\n\n" - "The following exception was thrown by Linode when trying to " - "run the initial deployment:\n%s", - name, - err, - exc_info_on_loglevel=logging.DEBUG, - ) - return False - - if "ERRORARRAY" in result: - for error_data in result["ERRORARRAY"]: - log.error( - "Error creating %s on Linode\n\n" - "The Linode API returned the following: %s\n", - name, - error_data["ERRORMESSAGE"], - ) - return False - - __utils__["cloud.fire_event"]( - "event", - "requesting instance", - "salt/cloud/{}/requesting".format(name), - args=__utils__["cloud.filter_event"]( - "requesting", vm_, ["name", "profile", "provider", "driver"] - ), - sock_dir=__opts__["sock_dir"], - transport=__opts__["transport"], - ) - - node_id = self._clean_data(result)["LinodeID"] - data["id"] = node_id - - if not self._wait_for_status( - node_id, status=(self._get_status_id_by_name("brand_new")) - ): - log.error( - "Error creating %s on LINODE\n\nwhile waiting for initial ready status", - name, - exc_info_on_loglevel=logging.DEBUG, - ) - - # Update the Linode's Label to reflect the given VM name - self._update_linode(node_id, update_args={"Label": name}) - log.debug("Set name for %s - was linode%s.", name, node_id) - - # Add private IP address if requested - private_ip_assignment = _get_private_ip(vm_) - if private_ip_assignment: - self._create_private_ip(node_id) - - # Define which ssh_interface to use - ssh_interface = _get_ssh_interface(vm_) - - # If ssh_interface is set to use private_ips, but assign_private_ip - # wasn't set to True, let's help out and create a private ip. - if ssh_interface == "private_ips" and private_ip_assignment is False: - self._create_private_ip(node_id) - private_ip_assignment = True - - if cloning: - config_id = get_config_id(kwargs={"linode_id": node_id})["config_id"] - else: - # Create disks and get ids - log.debug("Creating disks for %s", name) - root_disk_id = self._create_disk_from_distro(vm_, node_id)["DiskID"] - swap_disk_id = self._create_swap_disk(vm_, node_id)["DiskID"] - - # Create a ConfigID using disk ids - config_id = create_config( - kwargs={ - "name": name, - "linode_id": node_id, - "root_disk_id": root_disk_id, - "swap_disk_id": swap_disk_id, - } - )["ConfigID"] - - # Boot the Linode - self.boot( - kwargs={ - "linode_id": node_id, - "config_id": config_id, - "check_running": False, - } - ) - - node_data = get_linode(kwargs={"linode_id": node_id}) - ips = self._get_ips(node_id) - state = int(node_data["STATUS"]) - - data["image"] = kwargs["image"] - data["name"] = name - data["size"] = size - data["state"] = self._get_status_descr_by_id(state) - data["private_ips"] = ips["private_ips"] - data["public_ips"] = ips["public_ips"] - - # Pass the correct IP address to the bootstrap ssh_host key - if ssh_interface == "private_ips": - vm_["ssh_host"] = data["private_ips"][0] - else: - vm_["ssh_host"] = data["public_ips"][0] - - # If a password wasn't supplied in the profile or provider config, set it now. - vm_["password"] = _get_password(vm_) - - # Make public_ips and private_ips available to the bootstrap script. - vm_["public_ips"] = ips["public_ips"] - vm_["private_ips"] = ips["private_ips"] - - # Send event that the instance has booted. - __utils__["cloud.fire_event"]( - "event", - "waiting for ssh", - "salt/cloud/{}/waiting_for_ssh".format(name), - sock_dir=__opts__["sock_dir"], - args={"ip_address": vm_["ssh_host"]}, - transport=__opts__["transport"], - ) - - # Bootstrap! - ret = __utils__["cloud.bootstrap"](vm_, __opts__) - - ret.update(data) - - log.info("Created Cloud VM '%s'", name) - log.debug("'%s' VM creation details:\n%s", name, pprint.pformat(data)) - - __utils__["cloud.fire_event"]( - "event", - "created instance", - "salt/cloud/{}/created".format(name), - args=__utils__["cloud.filter_event"]( - "created", vm_, ["name", "profile", "provider", "driver"] - ), - sock_dir=__opts__["sock_dir"], - transport=__opts__["transport"], - ) - - return ret - - def create_config(self, kwargs=None): - name = kwargs.get("name", None) - linode_id = kwargs.get("linode_id", None) - root_disk_id = kwargs.get("root_disk_id", None) - swap_disk_id = kwargs.get("swap_disk_id", None) - data_disk_id = kwargs.get("data_disk_id", None) - kernel_id = kwargs.get("kernel_id", None) - - if kernel_id is None: - # 138 appears to always be the latest 64-bit kernel for Linux - kernel_id = 138 - - required_params = [name, linode_id, root_disk_id, swap_disk_id] - for item in required_params: - if item is None: - raise SaltCloudSystemExit( - "The create_config functions requires a 'name', 'linode_id', " - "'root_disk_id', and 'swap_disk_id'." - ) - - if kernel_id is None: - # 138 appears to always be the latest 64-bit kernel for Linux - kernel_id = 138 - - if not linode_id: - instance = self._get_linode_by_name(name) - linode_id = instance.get("id", None) - - disklist = "{},{}".format(root_disk_id, swap_disk_id) - if data_disk_id is not None: - disklist = "{},{},{}".format(root_disk_id, swap_disk_id, data_disk_id) - - config_args = { - "LinodeID": int(linode_id), - "KernelID": int(kernel_id), - "Label": name, - "DiskList": disklist, - } - - result = self._query("linode", "config.create", args=config_args) - - return result.get("DATA", None) - - def _create_disk_from_distro(self, vm_, linode_id): - kwargs = {} - swap_size = _get_swap_size(vm_) - pub_key = _get_ssh_key(vm_) - root_password = _get_password(vm_) - - if pub_key: - kwargs.update({"rootSSHKey": pub_key}) - if root_password: - kwargs.update({"rootPass": root_password}) - else: - raise SaltCloudConfigError("The Linode driver requires a password.") - - kwargs.update( - { - "LinodeID": linode_id, - "DistributionID": self._get_distribution_id(vm_), - "Label": vm_["name"], - "Size": self._get_disk_size(vm_, swap_size, linode_id), - } - ) - - result = self._query("linode", "disk.createfromdistribution", args=kwargs) - - return self._clean_data(result) - - def _create_swap_disk(self, vm_, linode_id, swap_size=None): - r""" - Creates the disk for the specified Linode. - - vm\_ - The VM profile to create the swap disk for. - - linode_id - The ID of the Linode to create the swap disk for. - - swap_size - The size of the disk, in MB. - """ - kwargs = {} - - if not swap_size: - swap_size = _get_swap_size(vm_) - - kwargs.update( - { - "LinodeID": linode_id, - "Label": vm_["name"], - "Type": "swap", - "Size": swap_size, - } - ) - - result = self._query("linode", "disk.create", args=kwargs) - - return self._clean_data(result) - - def _create_data_disk(self, vm_=None, linode_id=None, data_size=None): - kwargs = {} - - kwargs.update( - { - "LinodeID": linode_id, - "Label": vm_["name"] + "_data", - "Type": "ext4", - "Size": data_size, - } - ) - - result = self._query("linode", "disk.create", args=kwargs) - return self._clean_data(result) - - def _create_private_ip(self, linode_id): - r""" - Creates a private IP for the specified Linode. - - linode_id - The ID of the Linode to create the IP address for. - """ - kwargs = {"LinodeID": linode_id} - result = self._query("linode", "ip.addprivate", args=kwargs) - - return self._clean_data(result) - - def destroy(self, name): - __utils__["cloud.fire_event"]( - "event", - "destroying instance", - "salt/cloud/{}/destroying".format(name), - args={"name": name}, - sock_dir=__opts__["sock_dir"], - transport=__opts__["transport"], - ) - - linode_id = self._get_linode_id_from_name(name) - - response = self._query( - "linode", "delete", args={"LinodeID": linode_id, "skipChecks": True} - ) - - __utils__["cloud.fire_event"]( - "event", - "destroyed instance", - "salt/cloud/{}/destroyed".format(name), - args={"name": name}, - sock_dir=__opts__["sock_dir"], - transport=__opts__["transport"], - ) - - if __opts__.get("update_cachedir", False) is True: - __utils__["cloud.delete_minion_cachedir"]( - name, _get_active_provider_name().split(":")[0], __opts__ - ) - - return response - - def _decode_linode_plan_label(self, label): - """ - Attempts to decode a user-supplied Linode plan label - into the format in Linode API output - - label - The label, or name, of the plan to decode. - - Example: - `Linode 2048` will decode to `Linode 2GB` - """ - sizes = self.avail_sizes() - - if label not in sizes: - if "GB" in label: - raise SaltCloudException( - "Invalid Linode plan ({}) specified - call avail_sizes() for all" - " available options".format(label) - ) - else: - plan = label.split() - - if len(plan) != 2: - raise SaltCloudException( - "Invalid Linode plan ({}) specified - call avail_sizes() for" - " all available options".format(label) - ) - - plan_type = plan[0] - try: - plan_size = int(plan[1]) - except TypeError: - plan_size = 0 - log.debug( - "Failed to decode Linode plan label in Cloud Profile: %s", label - ) - - if plan_type == "Linode" and plan_size == 1024: - plan_type = "Nanode" - - plan_size = plan_size / 1024 - new_label = "{} {}GB".format(plan_type, plan_size) - - if new_label not in sizes: - raise SaltCloudException( - "Invalid Linode plan ({}) specified - call avail_sizes() for" - " all available options".format(new_label) - ) - - log.warning( - "An outdated Linode plan label was detected in your Cloud " - "Profile (%s). Please update the profile to use the new " - "label format (%s) for the requested Linode plan size.", - label, - new_label, - ) - - label = new_label - - return sizes[label]["PLANID"] - - def get_config_id(self, kwargs=None): - name = kwargs.get("name", None) - linode_id = kwargs.get("linode_id", None) - if name is None and linode_id is None: - raise SaltCloudSystemExit( - "The get_config_id function requires either a 'name' or a 'linode_id' " - "to be provided." - ) - if linode_id is None: - linode_id = self._get_linode_id_from_name(name) - - response = self._query("linode", "config.list", args={"LinodeID": linode_id})[ - "DATA" - ] - config_id = {"config_id": response[0]["ConfigID"]} - - return config_id - - def _get_datacenter_id(self, location): - """ - Returns the Linode Datacenter ID. - - location - The location, or name, of the datacenter to get the ID from. - """ - return avail_locations()[location]["DATACENTERID"] - - def _get_disk_size(self, vm_, swap, linode_id): - r""" - Returns the size of of the root disk in MB. - - vm\_ - The VM to get the disk size for. - """ - disk_size = get_linode(kwargs={"linode_id": linode_id})["TOTALHD"] - return config.get_cloud_config_value( - "disk_size", vm_, __opts__, default=disk_size - swap - ) - - def _get_distribution_id(self, vm_): - r""" - Returns the distribution ID for a VM - - vm\_ - The VM to get the distribution ID for - """ - distributions = self._query("avail", "distributions")["DATA"] - vm_image_name = config.get_cloud_config_value("image", vm_, __opts__) - - distro_id = "" - - for distro in distributions: - if vm_image_name == distro["LABEL"]: - distro_id = distro["DISTRIBUTIONID"] - return distro_id - - if not distro_id: - raise SaltCloudNotFound( - "The DistributionID for the '{}' profile could not be found.\nThe '{}'" - " instance could not be provisioned. The following distributions are" - " available:\n{}".format( - vm_image_name, - vm_["name"], - pprint.pprint( - sorted( - distro["LABEL"].encode(__salt_system_encoding__) - for distro in distributions - ) - ), - ) - ) - - def get_plan_id(self, kwargs=None): - label = kwargs.get("label", None) - if label is None: - raise SaltCloudException("The get_plan_id function requires a 'label'.") - return self._decode_linode_plan_label(label) - - def _get_ips(self, linode_id=None): - """ - Returns public and private IP addresses. - - linode_id - Limits the IP addresses returned to the specified Linode ID. - """ - if linode_id: - ips = self._query("linode", "ip.list", args={"LinodeID": linode_id}) - else: - ips = self._query("linode", "ip.list") - - ips = ips["DATA"] - ret = {} - - for item in ips: - node_id = str(item["LINODEID"]) - if item["ISPUBLIC"] == 1: - key = "public_ips" - else: - key = "private_ips" - - if ret.get(node_id) is None: - ret.update({node_id: {"public_ips": [], "private_ips": []}}) - ret[node_id][key].append(item["IPADDRESS"]) - - # If linode_id was specified, only return the ips, and not the - # dictionary based on the linode ID as a key. - if linode_id: - _all_ips = {"public_ips": [], "private_ips": []} - matching_id = ret.get(str(linode_id)) - if matching_id: - _all_ips["private_ips"] = matching_id["private_ips"] - _all_ips["public_ips"] = matching_id["public_ips"] - - ret = _all_ips - - return ret - - def _wait_for_job(self, linode_id, job_id, timeout=300, quiet=True): - """ - Wait for a Job to return. - - linode_id - The ID of the Linode to wait on. Required. - - job_id - The ID of the job to wait for. - - timeout - The amount of time to wait for a status to update. - - quiet - Log status updates to debug logs when True. Otherwise, logs to info. - """ - interval = 5 - iterations = int(timeout / interval) - - for i in range(0, iterations): - jobs_result = self._query( - "linode", "job.list", args={"LinodeID": linode_id} - )["DATA"] - if ( - jobs_result[0]["JOBID"] == job_id - and jobs_result[0]["HOST_SUCCESS"] == 1 - ): - return True - - time.sleep(interval) - log.log( - logging.INFO if not quiet else logging.DEBUG, - "Still waiting on Job %s for Linode %s.", - job_id, - linode_id, - ) - return False - - def _wait_for_status(self, linode_id, status=None, timeout=300, quiet=True): - """ - Wait for a certain status from Linode. - - linode_id - The ID of the Linode to wait on. Required. - - status - The status to look for to update. - - timeout - The amount of time to wait for a status to update. - - quiet - Log status updates to debug logs when False. Otherwise, logs to info. - """ - if status is None: - status = self._get_status_id_by_name("brand_new") - - status_desc_waiting = self._get_status_descr_by_id(status) - - interval = 5 - iterations = int(timeout / interval) - - for i in range(0, iterations): - result = get_linode(kwargs={"linode_id": linode_id}) - - if result["STATUS"] == status: - return True - - status_desc_result = self._get_status_descr_by_id(result["STATUS"]) - - time.sleep(interval) - log.log( - logging.INFO if not quiet else logging.DEBUG, - "Status for Linode %s is '%s', waiting for '%s'.", - linode_id, - status_desc_result, - status_desc_waiting, - ) - - return False - - def _list_linodes(self, full=False): - nodes = self._query("linode", "list")["DATA"] - ips = self._get_ips() - - ret = {} - for node in nodes: - this_node = {} - linode_id = str(node["LINODEID"]) - - this_node["id"] = linode_id - this_node["image"] = node["DISTRIBUTIONVENDOR"] - this_node["name"] = node["LABEL"] - this_node["size"] = node["TOTALRAM"] - - state = int(node["STATUS"]) - this_node["state"] = self._get_status_descr_by_id(state) - - for key, val in ips.items(): - if key == linode_id: - this_node["private_ips"] = val["private_ips"] - this_node["public_ips"] = val["public_ips"] - - if full: - this_node["extra"] = node - - ret[node["LABEL"]] = this_node - - return ret - - def list_nodes(self): - return self._list_linodes() - - def list_nodes_full(self): - return self._list_linodes(full=True) - - def list_nodes_min(self): - ret = {} - nodes = self._query("linode", "list")["DATA"] - - for node in nodes: - name = node["LABEL"] - ret[name] = { - "id": str(node["LINODEID"]), - "state": self._get_status_descr_by_id(int(node["STATUS"])), - } - return ret - - def show_instance(self, name): - node_id = self._get_linode_id_from_name(name) - node_data = get_linode(kwargs={"linode_id": node_id}) - ips = self._get_ips(node_id) - state = int(node_data["STATUS"]) - - return { - "id": node_data["LINODEID"], - "image": node_data["DISTRIBUTIONVENDOR"], - "name": node_data["LABEL"], - "size": node_data["TOTALRAM"], - "state": self._get_status_descr_by_id(state), - "private_ips": ips["private_ips"], - "public_ips": ips["public_ips"], - } - - def show_pricing(self, kwargs=None): - profile = __opts__["profiles"].get(kwargs["profile"], {}) - if not profile: - raise SaltCloudNotFound("The requested profile was not found.") - - # Make sure the profile belongs to Linode - provider = profile.get("provider", "0:0") - comps = provider.split(":") - if len(comps) < 2 or comps[1] != "linode": - raise SaltCloudException("The requested profile does not belong to Linode.") - - plan_id = self.get_plan_id(kwargs={"label": profile["size"]}) - response = self._query("avail", "linodeplans", args={"PlanID": plan_id})[ - "DATA" - ][0] - - ret = {} - ret["per_hour"] = response["HOURLY"] - ret["per_day"] = ret["per_hour"] * 24 - ret["per_week"] = ret["per_day"] * 7 - ret["per_month"] = response["PRICE"] - ret["per_year"] = ret["per_month"] * 12 - return {profile["profile"]: ret} - - def _update_linode(self, linode_id, update_args=None): - update_args.update({"LinodeID": linode_id}) - result = self._query("linode", "update", args=update_args) - return self._clean_data(result) - - def _get_linode_id_from_name(self, name): - node = self._get_linode_by_name(name) - return node.get("LINODEID", None) - - def _get_linode_by_name(self, name): - nodes = self._query("linode", "list")["DATA"] - for node in nodes: - if name == node["LABEL"]: - return node - - raise SaltCloudNotFound( - "The specified name, {}, could not be found.".format(name) - ) - - def _get_linode_by_id(self, linode_id): - result = self._query("linode", "list", args={"LinodeID": linode_id}) - return result["DATA"][0] - - def start(self, name): - node_id = self._get_linode_id_from_name(name) - node = get_linode(kwargs={"linode_id": node_id}) - - if node["STATUS"] == 1: - return { - "success": True, - "action": "start", - "state": "Running", - "msg": "Machine already running", - } - - response = self._query("linode", "boot", args={"LinodeID": node_id})["DATA"] - if self._wait_for_job(node_id, response["JobID"]): - return {"state": "Running", "action": "start", "success": True} - else: - return {"action": "start", "success": False} - - def stop(self, name): - node_id = self._get_linode_id_from_name(name) - node = get_linode(kwargs={"linode_id": node_id}) - - if node["STATUS"] == 2: - return { - "success": True, - "state": "Stopped", - "msg": "Machine already stopped", - } - - response = self._query("linode", "shutdown", args={"LinodeID": node_id})["DATA"] - - if self._wait_for_job(node_id, response["JobID"]): - return {"state": "Stopped", "action": "stop", "success": True} - return {"action": "stop", "success": False} - - def reboot(self, name): - node_id = self._get_linode_id_from_name(name) - response = self._query("linode", "reboot", args={"LinodeID": node_id}) - data = self._clean_data(response) - reboot_jid = data["JobID"] - - if not self._wait_for_job(node_id, reboot_jid): - log.error("Reboot failed for %s.", name) - return False - - return data - - def _clean_data(self, api_response): - """ - Returns the DATA response from a Linode API query as a single pre-formatted dictionary - - api_response - The query to be cleaned. - """ - data = {} - data.update(api_response["DATA"]) - - if not data: - response_data = api_response["DATA"] - data.update(response_data) - - return data - - def _get_status_descr_by_id(self, status_id): - """ - Return linode status by ID - - status_id - linode VM status ID - """ - for status_name, status_data in LINODE_STATUS.items(): - if status_data["code"] == int(status_id): - return status_data["descr"] - return LINODE_STATUS.get(status_id, None) - - def _get_status_id_by_name(self, status_name): - """ - Return linode status description by internalstatus name - - status_name - internal linode VM status name - """ - return LINODE_STATUS.get(status_name, {}).get("code", None) - - def avail_images(call=None): """ Return available Linode images. @@ -2156,7 +1047,7 @@ def avail_images(call=None): raise SaltCloudException( "The avail_images function must be called with -f or --function." ) - return _get_cloud_interface().avail_images() + return LinodeAPIv4.get_api_instance().avail_images() def avail_locations(call=None): @@ -2174,7 +1065,7 @@ def avail_locations(call=None): raise SaltCloudException( "The avail_locations function must be called with -f or --function." ) - return _get_cloud_interface().avail_locations() + return LinodeAPIv4.get_api_instance().avail_locations() def avail_sizes(call=None): @@ -2192,7 +1083,7 @@ def avail_sizes(call=None): raise SaltCloudException( "The avail_locations function must be called with -f or --function." ) - return _get_cloud_interface().avail_sizes() + return LinodeAPIv4.get_api_instance().avail_sizes() def boot(name=None, kwargs=None, call=None): @@ -2243,7 +1134,7 @@ def boot(name=None, kwargs=None, call=None): "The boot function requires either a 'name' or a 'linode_id'." ) - return _get_cloud_interface().boot(name=name, kwargs=kwargs) + return LinodeAPIv4.get_api_instance().boot(name=name, kwargs=kwargs) def clone(kwargs=None, call=None): @@ -2259,26 +1150,18 @@ def clone(kwargs=None, call=None): size The size of the new Linode (must be greater than or equal to the clone source). Required. - datacenter_id - The ID of the Datacenter where the Linode will be placed. Required for APIv3 usage. - Deprecated. Use ``location`` instead. - - plan_id - The ID of the plan (size) of the Linode. Required. Required for APIv3 usage. - Deprecated. Use ``size`` instead. - CLI Example: .. code-block:: bash - salt-cloud -f clone my-linode-config linode_id=1234567 datacenter_id=2 plan_id=5 + salt-cloud -f clone my-linode-config linode_id=1234567 location=us-central size=g6-standard-1 """ if call == "action": raise SaltCloudSystemExit( "The clone function must be called with -f or --function." ) - return _get_cloud_interface().clone(kwargs=kwargs) + return LinodeAPIv4.get_api_instance().clone(kwargs=kwargs) def create(vm_): @@ -2301,7 +1184,7 @@ def create(vm_): except AttributeError: pass - return _get_cloud_interface().create(vm_) + return LinodeAPIv4.get_api_instance().create(vm_) def create_config(kwargs=None, call=None): @@ -2332,7 +1215,7 @@ def create_config(kwargs=None, call=None): raise SaltCloudSystemExit( "The create_config function must be called with -f or --function." ) - return _get_cloud_interface().create_config(kwargs=kwargs) + return LinodeAPIv4.get_api_instance().create_config(kwargs=kwargs) def destroy(name, call=None): @@ -2352,7 +1235,7 @@ def destroy(name, call=None): raise SaltCloudException( "The destroy action must be called with -d, --destroy, -a or --action." ) - return _get_cloud_interface().destroy(name) + return LinodeAPIv4.get_api_instance().destroy(name) def get_config_id(kwargs=None, call=None): @@ -2380,7 +1263,7 @@ def get_config_id(kwargs=None, call=None): raise SaltCloudException( "The get_config_id function must be called with -f or --function." ) - return _get_cloud_interface().get_config_id(kwargs=kwargs) + return LinodeAPIv4.get_api_instance().get_config_id(kwargs=kwargs) def get_linode(kwargs=None, call=None): @@ -2407,28 +1290,7 @@ def get_linode(kwargs=None, call=None): raise SaltCloudSystemExit( "The get_linode function must be called with -f or --function." ) - return _get_cloud_interface().get_linode(kwargs=kwargs) - - -def get_plan_id(kwargs=None, call=None): - """ - Returns the Linode Plan ID. - - label - The label, or name, of the plan to get the ID from. - - CLI Example: - - .. code-block:: bash - - salt-cloud -f get_plan_id linode label="Nanode 1GB" - salt-cloud -f get_plan_id linode label="Linode 2GB" - """ - if call == "action": - raise SaltCloudException( - "The show_instance action must be called with -f or --function." - ) - return _get_cloud_interface().get_plan_id(kwargs=kwargs) + return LinodeAPIv4.get_api_instance().get_linode(kwargs=kwargs) def list_nodes(call=None): @@ -2453,7 +1315,7 @@ def list_nodes(call=None): raise SaltCloudException( "The list_nodes function must be called with -f or --function." ) - return _get_cloud_interface().list_nodes() + return LinodeAPIv4.get_api_instance().list_nodes() def list_nodes_full(call=None): @@ -2478,7 +1340,7 @@ def list_nodes_full(call=None): raise SaltCloudException( "The list_nodes_full function must be called with -f or --function." ) - return _get_cloud_interface().list_nodes_full() + return LinodeAPIv4.get_api_instance().list_nodes_full() def list_nodes_min(call=None): @@ -2500,14 +1362,14 @@ def list_nodes_min(call=None): raise SaltCloudSystemExit( "The list_nodes_min function must be called with -f or --function." ) - return _get_cloud_interface().list_nodes_min() + return LinodeAPIv4.get_api_instance().list_nodes_min() def list_nodes_select(call=None): """ Return a list of the VMs that are on the provider, with select fields. """ - return _get_cloud_interface().list_nodes_select(call) + return LinodeAPIv4.get_api_instance().list_nodes_select(call) def reboot(name, call=None): @@ -2529,7 +1391,7 @@ def reboot(name, call=None): raise SaltCloudException( "The show_instance action must be called with -a or --action." ) - return _get_cloud_interface().reboot(name) + return LinodeAPIv4.get_api_instance().reboot(name) def show_instance(name, call=None): @@ -2558,7 +1420,7 @@ def show_instance(name, call=None): raise SaltCloudException( "The show_instance action must be called with -a or --action." ) - return _get_cloud_interface().show_instance(name) + return LinodeAPIv4.get_api_instance().show_instance(name) def show_pricing(kwargs=None, call=None): @@ -2578,7 +1440,7 @@ def show_pricing(kwargs=None, call=None): raise SaltCloudException( "The show_instance action must be called with -f or --function." ) - return _get_cloud_interface().show_pricing(kwargs=kwargs) + return LinodeAPIv4.get_api_instance().show_pricing(kwargs=kwargs) def start(name, call=None): @@ -2596,7 +1458,7 @@ def start(name, call=None): """ if call != "action": raise SaltCloudException("The start action must be called with -a or --action.") - return _get_cloud_interface().start(name) + return LinodeAPIv4.get_api_instance().start(name) def stop(name, call=None): @@ -2614,4 +1476,4 @@ def stop(name, call=None): """ if call != "action": raise SaltCloudException("The stop action must be called with -a or --action.") - return _get_cloud_interface().stop(name) + return LinodeAPIv4.get_api_instance().stop(name)