Edit file File name : etcd_return.py Content :""" Return data to an etcd server or cluster :depends: - python-etcd In order to return to an etcd server, a profile should be created in the master configuration file: .. code-block:: yaml my_etcd_config: etcd.host: 127.0.0.1 etcd.port: 2379 It is technically possible to configure etcd without using a profile, but this is not considered to be a best practice, especially when multiple etcd servers or clusters are available. .. code-block:: yaml etcd.host: 127.0.0.1 etcd.port: 2379 Additionally, two more options must be specified in the top-level configuration in order to use the etcd returner: .. code-block:: yaml etcd.returner: my_etcd_config etcd.returner_root: /salt/return The ``etcd.returner`` option specifies which configuration profile to use. The ``etcd.returner_root`` option specifies the path inside etcd to use as the root of the returner system. Once the etcd options are configured, the returner may be used: CLI Example: salt '*' test.ping --return etcd A username and password can be set: .. code-block:: yaml etcd.username: larry # Optional; requires etcd.password to be set etcd.password: 123pass # Optional; requires etcd.username to be set You can also set a TTL (time to live) value for the returner: .. code-block:: yaml etcd.ttl: 5 Authentication with username and password, and ttl, currently requires the ``master`` branch of ``python-etcd``. You may also specify different roles for read and write operations. First, create the profiles as specified above. Then add: .. code-block:: yaml etcd.returner_read_profile: my_etcd_read etcd.returner_write_profile: my_etcd_write """ import logging import salt.utils.jid import salt.utils.json try: import salt.utils.etcd_util HAS_LIBS = True except ImportError: HAS_LIBS = False log = logging.getLogger(__name__) # Define the module's virtual name __virtualname__ = "etcd" def __virtual__(): """ Only return if python-etcd is installed """ if HAS_LIBS: return __virtualname__ return False, "Could not import etcd returner; python-etcd is not installed." def _get_conn(opts, profile=None): """ Establish a connection to etcd """ if profile is None: profile = opts.get("etcd.returner") path = opts.get("etcd.returner_root", "/salt/return") return salt.utils.etcd_util.get_conn(opts, profile), path def returner(ret): """ Return data to an etcd server or cluster """ write_profile = __opts__.get("etcd.returner_write_profile") if write_profile: ttl = __opts__.get(write_profile, {}).get("etcd.ttl") else: ttl = __opts__.get("etcd.ttl") client, path = _get_conn(__opts__, write_profile) # Make a note of this minion for the external job cache client.set( "/".join((path, "minions", ret["id"])), ret["jid"], ttl=ttl, ) for field in ret: # Not using os.path.join because we're not dealing with file paths dest = "/".join((path, "jobs", ret["jid"], ret["id"], field)) client.set(dest, salt.utils.json.dumps(ret[field]), ttl=ttl) def save_load(jid, load, minions=None): """ Save the load to the specified jid """ log.debug("sdstack_etcd returner <save_load> called jid: %s", jid) write_profile = __opts__.get("etcd.returner_write_profile") client, path = _get_conn(__opts__, write_profile) if write_profile: ttl = __opts__.get(write_profile, {}).get("etcd.ttl") else: ttl = __opts__.get("etcd.ttl") client.set( "/".join((path, "jobs", jid, ".load.p")), salt.utils.json.dumps(load), ttl=ttl, ) def save_minions(jid, minions, syndic_id=None): # pylint: disable=unused-argument """ Included for API consistency """ def clean_old_jobs(): """ Included for API consistency """ def get_load(jid): """ Return the load data that marks a specified jid """ log.debug("sdstack_etcd returner <get_load> called jid: %s", jid) read_profile = __opts__.get("etcd.returner_read_profile") client, path = _get_conn(__opts__, read_profile) return salt.utils.json.loads( client.get("/".join((path, "jobs", jid, ".load.p"))).value ) def get_jid(jid): """ Return the information returned when the specified job id was executed """ log.debug("sdstack_etcd returner <get_jid> called jid: %s", jid) ret = {} client, path = _get_conn(__opts__) items = client.get("/".join((path, "jobs", jid))) for item in items.children: if str(item.key).endswith(".load.p"): continue comps = str(item.key).split("/") data = client.get("/".join((path, "jobs", jid, comps[-1], "return"))).value ret[comps[-1]] = {"return": salt.utils.json.loads(data)} return ret def get_fun(fun): """ Return a dict of the last function called for all minions """ log.debug("sdstack_etcd returner <get_fun> called fun: %s", fun) ret = {} client, path = _get_conn(__opts__) items = client.get("/".join((path, "minions"))) for item in items.children: comps = str(item.key).split("/") efun = salt.utils.json.loads( client.get( "/".join((path, "jobs", str(item.value), comps[-1], "fun")) ).value ) if efun == fun: ret[comps[-1]] = str(efun) return ret def get_jids(): """ Return a list of all job ids """ log.debug("sdstack_etcd returner <get_jids> called") ret = [] client, path = _get_conn(__opts__) items = client.get("/".join((path, "jobs"))) for item in items.children: if item.dir is True: jid = str(item.key).split("/")[-1] ret.append(jid) return ret def get_minions(): """ Return a list of minions """ log.debug("sdstack_etcd returner <get_minions> called") ret = [] client, path = _get_conn(__opts__) items = client.get("/".join((path, "minions"))) for item in items.children: comps = str(item.key).split("/") ret.append(comps[-1]) return ret def prep_jid(nocache=False, passed_jid=None): # pylint: disable=unused-argument """ Do any work necessary to prepare a JID, including sending a custom id """ return passed_jid if passed_jid is not None else salt.utils.jid.gen_jid(__opts__) Save