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/joyent.py |
""" Joyent Cloud Module =================== The Joyent Cloud module is used to interact with the Joyent cloud system. Set up the cloud configuration at ``/etc/salt/cloud.providers`` or ``/etc/salt/cloud.providers.d/joyent.conf``: .. code-block:: yaml my-joyent-config: driver: joyent # The Joyent login user user: fred # The Joyent user's password password: saltybacon # The location of the ssh private key that can log into the new VM private_key: /root/mykey.pem # The name of the private key keyname: mykey When creating your profiles for the joyent cloud, add the location attribute to the profile, this will automatically get picked up when performing tasks associated with that vm. An example profile might look like: .. code-block:: yaml joyent_512: provider: my-joyent-config size: g4-highcpu-512M image: centos-6 location: us-east-1 This driver can also be used with the Joyent SmartDataCenter project. More details can be found at: .. _`SmartDataCenter`: https://github.com/joyent/sdc Using SDC requires that an api_host_suffix is set. The default value for this is `.api.joyentcloud.com`. All characters, including the leading `.`, should be included: .. code-block:: yaml api_host_suffix: .api.myhostname.com :depends: PyCrypto """ import base64 import datetime import http.client import inspect import logging import os import pprint import salt.config as config import salt.utils.cloud import salt.utils.files import salt.utils.http import salt.utils.json import salt.utils.yaml from salt.exceptions import ( SaltCloudExecutionFailure, SaltCloudExecutionTimeout, SaltCloudNotFound, SaltCloudSystemExit, ) try: from M2Crypto import EVP HAS_REQUIRED_CRYPTO = True HAS_M2 = True except ImportError: HAS_M2 = False try: from Cryptodome.Hash import SHA256 from Cryptodome.Signature import PKCS1_v1_5 HAS_REQUIRED_CRYPTO = True except ImportError: try: from Crypto.Hash import SHA256 # nosec from Crypto.Signature import PKCS1_v1_5 # nosec HAS_REQUIRED_CRYPTO = True except ImportError: HAS_REQUIRED_CRYPTO = False # Get logging started log = logging.getLogger(__name__) __virtualname__ = "joyent" JOYENT_API_HOST_SUFFIX = ".api.joyentcloud.com" JOYENT_API_VERSION = "~7.2" JOYENT_LOCATIONS = { "us-east-1": "North Virginia, USA", "us-west-1": "Bay Area, California, USA", "us-sw-1": "Las Vegas, Nevada, USA", "eu-ams-1": "Amsterdam, Netherlands", } DEFAULT_LOCATION = "us-east-1" # joyent no longer reports on all data centers, so setting this value to true # causes the list_nodes function to get information on machines from all # data centers POLL_ALL_LOCATIONS = True VALID_RESPONSE_CODES = [ http.client.OK, http.client.ACCEPTED, http.client.CREATED, http.client.NO_CONTENT, ] # Only load in this module if the Joyent configurations are in place def __virtual__(): """ Check for Joyent configs """ if HAS_REQUIRED_CRYPTO is False: return False, "Either PyCrypto or Cryptodome needs to be installed." 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 __virtualname__, ("user", "password") ) def get_image(vm_): """ Return the image object to use """ images = avail_images() vm_image = config.get_cloud_config_value("image", vm_, __opts__) if vm_image and str(vm_image) in images: images[vm_image]["name"] = images[vm_image]["id"] return images[vm_image] raise SaltCloudNotFound(f"The specified image, '{vm_image}', could not be found.") def get_size(vm_): """ Return the VM's size object """ sizes = avail_sizes() vm_size = config.get_cloud_config_value("size", vm_, __opts__) if not vm_size: raise SaltCloudNotFound("No size specified for this VM.") if vm_size and str(vm_size) in sizes: return sizes[vm_size] raise SaltCloudNotFound(f"The specified size, '{vm_size}', could not be found.") def query_instance(vm_=None, call=None): """ Query an instance upon creation from the Joyent API """ if isinstance(vm_, str) and call == "action": vm_ = {"name": vm_, "provider": "joyent"} if call == "function": # Technically this function may be called other ways too, but it # definitely cannot be called with --function. raise SaltCloudSystemExit( "The query_instance action must be called with -a or --action." ) __utils__["cloud.fire_event"]( "event", "querying instance", "salt/cloud/{}/querying".format(vm_["name"]), sock_dir=__opts__["sock_dir"], transport=__opts__["transport"], ) def _query_ip_address(): data = show_instance(vm_["name"], call="action") if not data: log.error("There was an error while querying Joyent. Empty response") # Trigger a failure in the wait for IP function return False if isinstance(data, dict) and "error" in data: log.warning("There was an error in the query %s", data.get("error")) # Trigger a failure in the wait for IP function return False log.debug("Returned query data: %s", data) if "primaryIp" in data[1]: # Wait for SSH to be fully configured on the remote side if data[1]["state"] == "running": return data[1]["primaryIp"] return None try: data = salt.utils.cloud.wait_for_ip( _query_ip_address, 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=10 ), interval_multiplier=config.get_cloud_config_value( "wait_for_ip_interval_multiplier", vm_, __opts__, default=1 ), ) except (SaltCloudExecutionTimeout, SaltCloudExecutionFailure) as exc: try: # destroy(vm_['name']) pass except SaltCloudSystemExit: pass finally: raise SaltCloudSystemExit(str(exc)) return data def create(vm_): """ Create a single VM from a data dict CLI Example: .. code-block:: bash salt-cloud -p profile_name vm_name """ 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 "joyent", vm_["profile"], vm_=vm_, ) is False ): return False except AttributeError: pass key_filename = config.get_cloud_config_value( "private_key", vm_, __opts__, search_global=False, default=None ) __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 in %s", vm_["name"], vm_.get("location", DEFAULT_LOCATION) ) # added . for fqdn hostnames salt.utils.cloud.check_name(vm_["name"], "a-zA-Z0-9-.") kwargs = { "name": vm_["name"], "image": get_image(vm_), "size": get_size(vm_), "location": vm_.get("location", DEFAULT_LOCATION), } # Let's not assign a default here; only assign a network value if # one is explicitly configured if "networks" in vm_: kwargs["networks"] = vm_.get("networks") __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"], ) data = create_node(**kwargs) if data == {}: log.error("Error creating %s on JOYENT", vm_["name"]) return False query_instance(vm_) data = show_instance(vm_["name"], call="action") vm_["key_filename"] = key_filename vm_["ssh_host"] = data[1]["primaryIp"] __utils__["cloud.bootstrap"](vm_, __opts__) __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 data[1] def create_node(**kwargs): """ convenience function to make the rest api call for node creation. """ name = kwargs["name"] size = kwargs["size"] image = kwargs["image"] location = kwargs["location"] networks = kwargs.get("networks") tag = kwargs.get("tag") locality = kwargs.get("locality") metadata = kwargs.get("metadata") firewall_enabled = kwargs.get("firewall_enabled") create_data = { "name": name, "package": size["name"], "image": image["name"], } if networks is not None: create_data["networks"] = networks if locality is not None: create_data["locality"] = locality if metadata is not None: for key, value in metadata.items(): create_data[f"metadata.{key}"] = value if tag is not None: for key, value in tag.items(): create_data[f"tag.{key}"] = value if firewall_enabled is not None: create_data["firewall_enabled"] = firewall_enabled data = salt.utils.json.dumps(create_data) ret = query(command="my/machines", data=data, method="POST", location=location) if ret[0] in VALID_RESPONSE_CODES: return ret[1] else: log.error("Failed to create node %s: %s", name, ret[1]) return {} def destroy(name, call=None): """ destroy a machine by name :param name: name given to the machine :param call: call value in this case is 'action' :return: array of booleans , true if successfully stopped and true if successfully removed CLI Example: .. code-block:: bash salt-cloud -d 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"], transport=__opts__["transport"], ) node = get_node(name) ret = query( command="my/machines/{}".format(node["id"]), location=node["location"], method="DELETE", ) __utils__["cloud.fire_event"]( "event", "destroyed instance", f"salt/cloud/{name}/destroyed", 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 ret[0] in VALID_RESPONSE_CODES def reboot(name, call=None): """ reboot a machine by name :param name: name given to the machine :param call: call value in this case is 'action' :return: true if successful CLI Example: .. code-block:: bash salt-cloud -a reboot vm_name """ node = get_node(name) ret = take_action( name=name, call=call, method="POST", command="my/machines/{}".format(node["id"]), location=node["location"], data={"action": "reboot"}, ) return ret[0] in VALID_RESPONSE_CODES def stop(name, call=None): """ stop a machine by name :param name: name given to the machine :param call: call value in this case is 'action' :return: true if successful CLI Example: .. code-block:: bash salt-cloud -a stop vm_name """ node = get_node(name) ret = take_action( name=name, call=call, method="POST", command="my/machines/{}".format(node["id"]), location=node["location"], data={"action": "stop"}, ) return ret[0] in VALID_RESPONSE_CODES def start(name, call=None): """ start a machine by name :param name: name given to the machine :param call: call value in this case is 'action' :return: true if successful CLI Example: .. code-block:: bash salt-cloud -a start vm_name """ node = get_node(name) ret = take_action( name=name, call=call, method="POST", command="my/machines/{}".format(node["id"]), location=node["location"], data={"action": "start"}, ) return ret[0] in VALID_RESPONSE_CODES def take_action( name=None, call=None, command=None, data=None, method="GET", location=DEFAULT_LOCATION, ): """ take action call used by start,stop, reboot :param name: name given to the machine :param call: call value in this case is 'action' :command: api path :data: any data to be passed to the api, must be in json format :method: GET,POST,or DELETE :location: data center to execute the command on :return: true if successful """ caller = inspect.stack()[1][3] if call != "action": raise SaltCloudSystemExit("This action must be called with -a or --action.") if data: data = salt.utils.json.dumps(data) ret = [] try: ret = query(command=command, data=data, method=method, location=location) log.info("Success %s for node %s", caller, name) except Exception as exc: # pylint: disable=broad-except if "InvalidState" in str(exc): ret = [200, {}] else: log.error( "Failed to invoke %s node %s: %s", caller, name, exc, # Show the traceback if the debug logging level is enabled exc_info_on_loglevel=logging.DEBUG, ) ret = [100, {}] return ret def ssh_interface(vm_): """ Return the ssh_interface type to connect to. Either 'public_ips' (default) or 'private_ips'. """ return config.get_cloud_config_value( "ssh_interface", vm_, __opts__, default="public_ips", search_global=False ) def get_location(vm_=None): """ Return the joyent data center to use, in this order: - CLI parameter - VM parameter - Cloud profile setting """ return __opts__.get( "location", config.get_cloud_config_value( "location", vm_ or get_configured_provider(), __opts__, default=DEFAULT_LOCATION, search_global=False, ), ) def avail_locations(call=None): """ List all available locations """ if call == "action": raise SaltCloudSystemExit( "The avail_locations function must be called with " "-f or --function, or with the --list-locations option" ) ret = {} for key in JOYENT_LOCATIONS: ret[key] = {"name": key, "region": JOYENT_LOCATIONS[key]} # this can be enabled when the bug in the joyent get data centers call is # corrected, currently only the European dc (new api) returns the correct # values # ret = {} # rcode, datacenters = query( # command='my/datacenters', location=DEFAULT_LOCATION, method='GET' # ) # if rcode in VALID_RESPONSE_CODES and isinstance(datacenters, dict): # for key in datacenters: # ret[key] = { # 'name': key, # 'url': datacenters[key] # } return ret def has_method(obj, method_name): """ Find if the provided object has a specific method """ if method_name in dir(obj): return True log.error("Method '%s' not yet supported!", method_name) return False def key_list(items=None): """ convert list to dictionary using the key as the identifier :param items: array to iterate over :return: dictionary """ if items is None: items = [] ret = {} if items and isinstance(items, list): for item in items: if "name" in item: # added for consistency with old code if "id" not in item: item["id"] = item["name"] ret[item["name"]] = item return ret def get_node(name): """ gets the node from the full node list by name :param name: name of the vm :return: node object """ nodes = list_nodes() if name in nodes: return nodes[name] return None def show_instance(name, call=None): """ get details about a machine :param name: name given to the machine :param call: call value in this case is 'action' :return: machine information CLI Example: .. code-block:: bash salt-cloud -a show_instance vm_name """ node = get_node(name) ret = query( command="my/machines/{}".format(node["id"]), location=node["location"], method="GET", ) return ret def _old_libcloud_node_state(id_): """ Libcloud supported node states """ states_int = { 0: "RUNNING", 1: "REBOOTING", 2: "TERMINATED", 3: "PENDING", 4: "UNKNOWN", 5: "STOPPED", 6: "SUSPENDED", 7: "ERROR", 8: "PAUSED", } states_str = { "running": "RUNNING", "rebooting": "REBOOTING", "starting": "STARTING", "terminated": "TERMINATED", "pending": "PENDING", "unknown": "UNKNOWN", "stopping": "STOPPING", "stopped": "STOPPED", "suspended": "SUSPENDED", "error": "ERROR", "paused": "PAUSED", "reconfiguring": "RECONFIGURING", } return states_str[id_] if isinstance(id_, str) else states_int[id_] def joyent_node_state(id_): """ Convert joyent returned state to state common to other data center return values for consistency :param id_: joyent state value :return: state value """ states = { "running": 0, "stopped": 2, "stopping": 2, "provisioning": 3, "deleted": 2, "unknown": 4, } if id_ not in states: id_ = "unknown" return _old_libcloud_node_state(states[id_]) def reformat_node(item=None, full=False): """ Reformat the returned data from joyent, determine public/private IPs and strip out fields if necessary to provide either full or brief content. :param item: node dictionary :param full: full or brief output :return: dict """ desired_keys = [ "id", "name", "state", "public_ips", "private_ips", "size", "image", "location", ] item["private_ips"] = [] item["public_ips"] = [] if "ips" in item: for ip in item["ips"]: if salt.utils.cloud.is_public_ip(ip): item["public_ips"].append(ip) else: item["private_ips"].append(ip) # add any undefined desired keys for key in desired_keys: if key not in item: item[key] = None # remove all the extra key value pairs to provide a brief listing to_del = [] if not full: for key in item.keys(): # iterate over a copy of the keys if key not in desired_keys: to_del.append(key) for key in to_del: del item[key] if "state" in item: item["state"] = joyent_node_state(item["state"]) return item def list_nodes(full=False, call=None): """ list of nodes, keeping only a brief listing CLI Example: .. code-block:: bash salt-cloud -Q """ if call == "action": raise SaltCloudSystemExit( "The list_nodes function must be called with -f or --function." ) ret = {} if POLL_ALL_LOCATIONS: for location in JOYENT_LOCATIONS: result = query(command="my/machines", location=location, method="GET") if result[0] in VALID_RESPONSE_CODES: nodes = result[1] for node in nodes: if "name" in node: node["location"] = location ret[node["name"]] = reformat_node(item=node, full=full) else: log.error("Invalid response when listing Joyent nodes: %s", result[1]) else: location = get_location() result = query(command="my/machines", location=location, method="GET") nodes = result[1] for node in nodes: if "name" in node: node["location"] = location ret[node["name"]] = reformat_node(item=node, full=full) return ret def list_nodes_full(call=None): """ list of nodes, maintaining all content provided from joyent listings CLI Example: .. code-block:: bash salt-cloud -F """ 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 """ return salt.utils.cloud.list_nodes_select( list_nodes_full("function"), __opts__["query.selection"], call, ) def _get_proto(): """ Checks configuration to see whether the user has SSL turned on. Default is: .. code-block:: yaml use_ssl: True """ use_ssl = config.get_cloud_config_value( "use_ssl", get_configured_provider(), __opts__, search_global=False, default=True, ) if use_ssl is True: return "https" return "http" def avail_images(call=None): """ Get list of available images CLI Example: .. code-block:: bash salt-cloud --list-images Can use a custom URL for images. Default is: .. code-block:: yaml image_url: images.joyent.com/images """ if call == "action": raise SaltCloudSystemExit( "The avail_images function must be called with " "-f or --function, or with the --list-images option" ) user = config.get_cloud_config_value( "user", get_configured_provider(), __opts__, search_global=False ) img_url = config.get_cloud_config_value( "image_url", get_configured_provider(), __opts__, search_global=False, default=f"{DEFAULT_LOCATION}{JOYENT_API_HOST_SUFFIX}/{user}/images", ) if not img_url.startswith("http://") and not img_url.startswith("https://"): img_url = f"{_get_proto()}://{img_url}" rcode, data = query(command="my/images", method="GET") log.debug(data) ret = {} for image in data: ret[image["name"]] = image return ret def avail_sizes(call=None): """ get list of available packages CLI Example: .. code-block:: bash salt-cloud --list-sizes """ if call == "action": raise SaltCloudSystemExit( "The avail_sizes function must be called with " "-f or --function, or with the --list-sizes option" ) rcode, items = query(command="my/packages") if rcode not in VALID_RESPONSE_CODES: return {} return key_list(items=items) def list_keys(kwargs=None, call=None): """ List the keys available """ if call != "function": log.error("The list_keys function must be called with -f or --function.") return False if not kwargs: kwargs = {} ret = {} rcode, data = query(command="my/keys", method="GET") for pair in data: ret[pair["name"]] = pair["key"] return {"keys": ret} def show_key(kwargs=None, call=None): """ List the keys available """ if call != "function": log.error("The list_keys function must be called with -f or --function.") return False if not kwargs: kwargs = {} if "keyname" not in kwargs: log.error("A keyname is required.") return False rcode, data = query( command="my/keys/{}".format(kwargs["keyname"]), method="GET", ) return {"keys": {data["name"]: data["key"]}} def import_key(kwargs=None, call=None): """ List the keys available CLI Example: .. code-block:: bash salt-cloud -f import_key joyent keyname=mykey keyfile=/tmp/mykey.pub """ if call != "function": log.error("The import_key function must be called with -f or --function.") return False if not kwargs: kwargs = {} if "keyname" not in kwargs: log.error("A keyname is required.") return False if "keyfile" not in kwargs: log.error("The location of the SSH keyfile is required.") return False if not os.path.isfile(kwargs["keyfile"]): log.error("The specified keyfile (%s) does not exist.", kwargs["keyfile"]) return False with salt.utils.files.fopen(kwargs["keyfile"], "r") as fp_: kwargs["key"] = salt.utils.stringutils.to_unicode(fp_.read()) send_data = {"name": kwargs["keyname"], "key": kwargs["key"]} kwargs["data"] = salt.utils.json.dumps(send_data) rcode, data = query( command="my/keys", method="POST", data=kwargs["data"], ) log.debug(pprint.pformat(data)) return {"keys": {data["name"]: data["key"]}} def delete_key(kwargs=None, call=None): """ List the keys available CLI Example: .. code-block:: bash salt-cloud -f delete_key joyent keyname=mykey """ if call != "function": log.error("The delete_keys function must be called with -f or --function.") return False if not kwargs: kwargs = {} if "keyname" not in kwargs: log.error("A keyname is required.") return False rcode, data = query( command="my/keys/{}".format(kwargs["keyname"]), method="DELETE", ) return data def get_location_path( location=DEFAULT_LOCATION, api_host_suffix=JOYENT_API_HOST_SUFFIX ): """ create url from location variable :param location: joyent data center location :return: url """ return f"{_get_proto()}://{location}{api_host_suffix}" def query(action=None, command=None, args=None, method="GET", location=None, data=None): """ Make a web call to Joyent """ user = config.get_cloud_config_value( "user", get_configured_provider(), __opts__, search_global=False ) if not user: log.error( "username is required for Joyent API requests. Please set one in your" " provider configuration" ) password = config.get_cloud_config_value( "password", get_configured_provider(), __opts__, search_global=False ) verify_ssl = config.get_cloud_config_value( "verify_ssl", get_configured_provider(), __opts__, search_global=False, default=True, ) ssh_keyfile = config.get_cloud_config_value( "private_key", get_configured_provider(), __opts__, search_global=False, default=True, ) if not ssh_keyfile: log.error( "ssh_keyfile is required for Joyent API requests. Please set one in your" " provider configuration" ) ssh_keyname = config.get_cloud_config_value( "keyname", get_configured_provider(), __opts__, search_global=False, default=True, ) if not ssh_keyname: log.error( "ssh_keyname is required for Joyent API requests. Please set one in your" " provider configuration" ) if not location: location = get_location() api_host_suffix = config.get_cloud_config_value( "api_host_suffix", get_configured_provider(), __opts__, search_global=False, default=JOYENT_API_HOST_SUFFIX, ) path = get_location_path(location=location, api_host_suffix=api_host_suffix) if action: path += action if command: path += f"/{command}" log.debug("User: '%s' on PATH: %s", user, path) if (not user) or (not ssh_keyfile) or (not ssh_keyname) or (not location): return None timenow = datetime.datetime.utcnow() timestamp = timenow.strftime("%a, %d %b %Y %H:%M:%S %Z").strip() rsa_key = salt.crypt.get_rsa_key(ssh_keyfile, None) if HAS_M2: md = EVP.MessageDigest("sha256") md.update(timestamp.encode(__salt_system_encoding__)) digest = md.final() signed = rsa_key.sign(digest, algo="sha256") else: rsa_ = PKCS1_v1_5.new(rsa_key) # pylint: disable=used-before-assignment hash_ = SHA256.new() # pylint: disable=used-before-assignment hash_.update(timestamp.encode(__salt_system_encoding__)) signed = rsa_.sign(hash_) signed = base64.b64encode(signed) user_arr = user.split("/") if len(user_arr) == 1: keyid = f"/{user_arr[0]}/keys/{ssh_keyname}" elif len(user_arr) == 2: keyid = f"/{user_arr[0]}/users/{user_arr[1]}/keys/{ssh_keyname}" else: log.error("Malformed user string") headers = { "Content-Type": "application/json", "Accept": "application/json", "X-Api-Version": JOYENT_API_VERSION, "Date": timestamp, "Authorization": 'Signature keyId="{}",algorithm="rsa-sha256" {}'.format( keyid, signed.decode(__salt_system_encoding__) ), } if not isinstance(args, dict): args = {} # post form data if not data: data = salt.utils.json.dumps({}) return_content = None result = salt.utils.http.query( path, method, params=args, header_dict=headers, data=data, decode=False, text=True, status=True, headers=True, verify_ssl=verify_ssl, opts=__opts__, ) log.debug("Joyent Response Status Code: %s", result["status"]) if "headers" not in result: return [result["status"], result["error"]] if "Content-Length" in result["headers"]: content = result["text"] return_content = salt.utils.yaml.safe_load(content) return [result["status"], return_content]