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/vultrpy.py |
""" Vultr Cloud Module using python-vultr bindings ============================================== .. versionadded:: 2016.3.0 The Vultr cloud module is used to control access to the Vultr VPS system. Use of this module only requires the ``api_key`` parameter. Set up the cloud configuration at ``/etc/salt/cloud.providers`` or ``/etc/salt/cloud.providers.d/vultr.conf``: .. code-block:: yaml my-vultr-config: # Vultr account api key api_key: <supersecretapi_key> driver: vultr Set up the cloud profile at ``/etc/salt/cloud.profiles`` or ``/etc/salt/cloud.profiles.d/vultr.conf``: .. code-block:: yaml nyc-4gb-4cpu-ubuntu-14-04: location: 1 provider: my-vultr-config image: 160 size: 95 enable_private_network: True This driver also supports Vultr's `startup script` feature. You can list startup scripts in your account with .. code-block:: bash salt-cloud -f list_scripts <name of vultr provider> That list will include the IDs of the scripts in your account. Thus, if you have a script called 'setup-networking' with an ID of 493234 you can specify that startup script in a profile like so: .. code-block:: yaml nyc-2gb-1cpu-ubuntu-17-04: location: 1 provider: my-vultr-config image: 223 size: 13 startup_script_id: 493234 Similarly you can also specify a fiewall group ID using the option firewall_group_id. You can list firewall groups with .. code-block:: bash salt-cloud -f list_firewall_groups <name of vultr provider> To specify SSH keys to be preinstalled on the server, use the ssh_key_names setting .. code-block:: yaml nyc-2gb-1cpu-ubuntu-17-04: location: 1 provider: my-vultr-config image: 223 size: 13 ssh_key_names: dev1,dev2,salt-master You can list SSH keys available on your account using .. code-block:: bash salt-cloud -f list_keypairs <name of vultr provider> """ import logging import pprint import time import urllib.parse import salt.config as config from salt.exceptions import SaltCloudConfigError, SaltCloudSystemExit # Get logging started log = logging.getLogger(__name__) __virtualname__ = "vultr" DETAILS = {} def __virtual__(): """ Set up the Vultr functions and check for configurations """ if get_configured_provider() 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 "vultr", ("api_key",) ) def _cache_provider_details(conn=None): """ Provide a place to hang onto results of --list-[locations|sizes|images] so we don't have to go out to the API and get them every time. """ DETAILS["avail_locations"] = {} DETAILS["avail_sizes"] = {} DETAILS["avail_images"] = {} locations = avail_locations(conn) images = avail_images(conn) sizes = avail_sizes(conn) for key, location in locations.items(): DETAILS["avail_locations"][location["name"]] = location DETAILS["avail_locations"][key] = location for key, image in images.items(): DETAILS["avail_images"][image["name"]] = image DETAILS["avail_images"][key] = image for key, vm_size in sizes.items(): DETAILS["avail_sizes"][vm_size["name"]] = vm_size DETAILS["avail_sizes"][key] = vm_size def avail_locations(conn=None): """ return available datacenter locations """ return _query("regions/list") def avail_scripts(conn=None): """ return available startup scripts """ return _query("startupscript/list") def avail_firewall_groups(conn=None): """ return available firewall groups """ return _query("firewall/group_list") def avail_keys(conn=None): """ return available SSH keys """ return _query("sshkey/list") def list_scripts(conn=None, call=None): """ return list of Startup Scripts """ return avail_scripts() def list_firewall_groups(conn=None, call=None): """ return list of firewall groups """ return avail_firewall_groups() def list_keypairs(conn=None, call=None): """ return list of SSH keys """ return avail_keys() def show_keypair(kwargs=None, call=None): """ return list of SSH keys """ if not kwargs: kwargs = {} if "keyname" not in kwargs: log.error("A keyname is required.") return False keys = list_keypairs(call="function") keyid = keys[kwargs["keyname"]]["SSHKEYID"] log.debug("Key ID is %s", keyid) return keys[kwargs["keyname"]] def avail_sizes(conn=None): """ Return available sizes ("plans" in VultrSpeak) """ return _query("plans/list") def avail_images(conn=None): """ Return available images """ return _query("os/list") def list_nodes(**kwargs): """ Return basic data on nodes """ ret = {} nodes = list_nodes_full() for node in nodes: ret[node] = {} for prop in "id", "image", "size", "state", "private_ips", "public_ips": ret[node][prop] = nodes[node][prop] return ret def list_nodes_full(**kwargs): """ Return all data on nodes """ nodes = _query("server/list") ret = {} for node in nodes: name = nodes[node]["label"] ret[name] = nodes[node].copy() ret[name]["id"] = node ret[name]["image"] = nodes[node]["os"] ret[name]["size"] = nodes[node]["VPSPLANID"] ret[name]["state"] = nodes[node]["status"] ret[name]["private_ips"] = nodes[node]["internal_ip"] ret[name]["public_ips"] = nodes[node]["main_ip"] return ret def list_nodes_select(conn=None, call=None): """ Return a list of the VMs that are on the provider, with select fields """ return __utils__["cloud.list_nodes_select"]( list_nodes_full(), __opts__["query.selection"], call, ) def destroy(name): """ Remove a node from Vultr """ node = show_instance(name, call="action") params = {"SUBID": node["SUBID"]} result = _query( "server/destroy", method="POST", decode=False, data=urllib.parse.urlencode(params), ) # The return of a destroy call is empty in the case of a success. # Errors are only indicated via HTTP status code. Status code 200 # effetively therefore means "success". if result.get("body") == "" and result.get("text") == "": return True return result def stop(*args, **kwargs): """ Execute a "stop" action on a VM """ return _query("server/halt") def start(*args, **kwargs): """ Execute a "start" action on a VM """ return _query("server/start") def show_instance(name, call=None): """ Show the details from the provider concerning an instance """ if call != "action": raise SaltCloudSystemExit( "The show_instance action must be called with -a or --action." ) nodes = list_nodes_full() # Find under which cloud service the name is listed, if any if name not in nodes: return {} __utils__["cloud.cache_node"](nodes[name], _get_active_provider_name(), __opts__) return nodes[name] def _lookup_vultrid(which_key, availkey, keyname): """ Helper function to retrieve a Vultr ID """ if DETAILS == {}: _cache_provider_details() which_key = str(which_key) try: return DETAILS[availkey][which_key][keyname] except KeyError: return False def create(vm_): """ Create a single VM from a data dict """ if "driver" not in vm_: vm_["driver"] = vm_["provider"] private_networking = config.get_cloud_config_value( "enable_private_network", vm_, __opts__, search_global=False, default=False, ) ssh_key_ids = config.get_cloud_config_value( "ssh_key_names", vm_, __opts__, search_global=False, default=None ) startup_script = config.get_cloud_config_value( "startup_script_id", vm_, __opts__, search_global=False, default=None, ) if startup_script and str(startup_script) not in avail_scripts(): log.error( "Your Vultr account does not have a startup script with ID %s", str(startup_script), ) return False firewall_group_id = config.get_cloud_config_value( "firewall_group_id", vm_, __opts__, search_global=False, default=None, ) if firewall_group_id and str(firewall_group_id) not in avail_firewall_groups(): log.error( "Your Vultr account does not have a firewall group with ID %s", str(firewall_group_id), ) return False if ssh_key_ids is not None: key_list = ssh_key_ids.split(",") available_keys = avail_keys() for key in key_list: if key and str(key) not in available_keys: log.error("Your Vultr account does not have a key with ID %s", str(key)) return False if private_networking is not None: if not isinstance(private_networking, bool): raise SaltCloudConfigError( "'private_networking' should be a boolean value." ) if private_networking is True: enable_private_network = "yes" else: enable_private_network = "no" __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"], ) osid = _lookup_vultrid(vm_["image"], "avail_images", "OSID") if not osid: log.error("Vultr does not have an image with id or name %s", vm_["image"]) return False vpsplanid = _lookup_vultrid(vm_["size"], "avail_sizes", "VPSPLANID") if not vpsplanid: log.error("Vultr does not have a size with id or name %s", vm_["size"]) return False dcid = _lookup_vultrid(vm_["location"], "avail_locations", "DCID") if not dcid: log.error("Vultr does not have a location with id or name %s", vm_["location"]) return False kwargs = { "label": vm_["name"], "OSID": osid, "VPSPLANID": vpsplanid, "DCID": dcid, "hostname": vm_["name"], "enable_private_network": enable_private_network, } if startup_script: kwargs["SCRIPTID"] = startup_script if firewall_group_id: kwargs["FIREWALLGROUPID"] = firewall_group_id if ssh_key_ids: kwargs["SSHKEYID"] = ssh_key_ids log.info("Creating Cloud VM %s", vm_["name"]) __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"], transport=__opts__["transport"], ) try: data = _query( "server/create", method="POST", data=urllib.parse.urlencode(kwargs) ) if int(data.get("status", "200")) >= 300: log.error( "Error creating %s on Vultr\n\nVultr API returned %s\n", vm_["name"], data, ) log.error( "Status 412 may mean that you are requesting an\n" "invalid location, image, or size." ) __utils__["cloud.fire_event"]( "event", "instance request failed", "salt/cloud/{}/requesting/failed".format(vm_["name"]), args={"kwargs": kwargs}, sock_dir=__opts__["sock_dir"], transport=__opts__["transport"], ) return False except Exception as exc: # pylint: disable=broad-except log.error( "Error creating %s on Vultr\n\n" "The following exception was thrown when trying to " "run the initial deployment:\n%s", vm_["name"], exc, # Show the traceback if the debug logging level is enabled exc_info_on_loglevel=logging.DEBUG, ) __utils__["cloud.fire_event"]( "event", "instance request failed", "salt/cloud/{}/requesting/failed".format(vm_["name"]), args={"kwargs": kwargs}, sock_dir=__opts__["sock_dir"], transport=__opts__["transport"], ) return False def wait_for_hostname(): """ Wait for the IP address to become available """ data = show_instance(vm_["name"], call="action") main_ip = str(data.get("main_ip", "0")) if main_ip.startswith("0"): time.sleep(3) return False return data["main_ip"] def wait_for_default_password(): """ Wait for the IP address to become available """ data = show_instance(vm_["name"], call="action") # print("Waiting for default password") # pprint.pprint(data) default_password = str(data.get("default_password", "")) if default_password == "" or default_password == "not supported": time.sleep(1) return False return data["default_password"] def wait_for_status(): """ Wait for the IP address to become available """ data = show_instance(vm_["name"], call="action") # print("Waiting for status normal") # pprint.pprint(data) if str(data.get("status", "")) != "active": time.sleep(1) return False return data["default_password"] def wait_for_server_state(): """ Wait for the IP address to become available """ data = show_instance(vm_["name"], call="action") # print("Waiting for server state ok") # pprint.pprint(data) if str(data.get("server_state", "")) != "ok": time.sleep(1) return False return data["default_password"] vm_["ssh_host"] = __utils__["cloud.wait_for_fun"]( wait_for_hostname, timeout=config.get_cloud_config_value( "wait_for_fun_timeout", vm_, __opts__, default=15 * 60 ), ) vm_["password"] = __utils__["cloud.wait_for_fun"]( wait_for_default_password, timeout=config.get_cloud_config_value( "wait_for_fun_timeout", vm_, __opts__, default=15 * 60 ), ) __utils__["cloud.wait_for_fun"]( wait_for_status, timeout=config.get_cloud_config_value( "wait_for_fun_timeout", vm_, __opts__, default=15 * 60 ), ) __utils__["cloud.wait_for_fun"]( wait_for_server_state, timeout=config.get_cloud_config_value( "wait_for_fun_timeout", vm_, __opts__, default=15 * 60 ), ) __opts__["hard_timeout"] = config.get_cloud_config_value( "hard_timeout", get_configured_provider(), __opts__, search_global=False, default=None, ) # Bootstrap ret = __utils__["cloud.bootstrap"](vm_, __opts__) ret.update(show_instance(vm_["name"], call="action")) 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"], transport=__opts__["transport"], ) return ret def _query(path, method="GET", data=None, params=None, header_dict=None, decode=True): """ Perform a query directly against the Vultr REST API """ api_key = config.get_cloud_config_value( "api_key", get_configured_provider(), __opts__, search_global=False, ) management_host = config.get_cloud_config_value( "management_host", get_configured_provider(), __opts__, search_global=False, default="api.vultr.com", ) url = "https://{management_host}/v1/{path}?api_key={api_key}".format( management_host=management_host, path=path, api_key=api_key, ) if header_dict is None: header_dict = {} result = __utils__["http.query"]( url, method=method, params=params, data=data, header_dict=header_dict, port=443, text=True, decode=decode, decode_type="json", hide_fields=["api_key"], opts=__opts__, ) if "dict" in result: return result["dict"] return result