%PDF- %PDF-
Direktori : /usr/lib/python2.7/site-packages/salt/utils/ |
Current File : //usr/lib/python2.7/site-packages/salt/utils/raetevent.py |
# -*- coding: utf-8 -*- ''' Manage events This module is used to manage events via RAET ''' # pylint: disable=3rd-party-module-not-gated # Import python libs from __future__ import absolute_import, print_function, unicode_literals import os import logging import time from collections import MutableMapping # Import salt libs import salt.payload import salt.loader import salt.state import salt.utils.event import salt.utils.kinds as kinds from salt import transport from salt import syspaths try: from raet import raeting, nacling from raet.lane.stacking import LaneStack from raet.lane.yarding import RemoteYard HAS_RAET = True except ImportError: HAS_RAET = False # Import 3rd-party libs from salt.ext import six log = logging.getLogger(__name__) def __virtual__(): return HAS_RAET class RAETEvent(object): ''' The base class used to manage salt events ''' def __init__(self, node, sock_dir=None, listen=True, opts=None): ''' Set up the stack and remote yard ''' self.node = node # application kind see kinds.APPL_KIND_NAMES self.sock_dir = sock_dir if opts is None: opts = {} self.opts = opts self.stack = None self.ryn = 'manor' # remote yard name self.connected = False self.cpub = False self.__load_cache_regex() self.__prep_stack(listen) @classmethod def __load_cache_regex(cls): ''' Initialize the regular expression cache and put it in the class namespace. The regex search strings will be prepend with '^' ''' # This is in the class namespace, to minimize cache memory # usage and maximize cache hits # The prepend='^' is to reduce differences in behavior between # the default 'startswith' and the optional 'regex' match_type cls.cache_regex = salt.utils.cache.CacheRegex(prepend='^') def __prep_stack(self, listen): ''' Prepare the stack objects ''' if not self.stack: if hasattr(transport, 'jobber_stack') and transport.jobber_stack: self.stack = transport.jobber_stack else: self.stack = transport.jobber_stack = self._setup_stack(ryn=self.ryn) log.debug("RAETEvent Using Jobber Stack at = %s\n", self.stack.ha) if listen: self.subscribe() def _setup_stack(self, ryn='manor'): kind = self.opts.get('__role', '') # opts optional for master if kind: # not all uses of Raet SaltEvent has opts defined if kind not in kinds.APPL_KINDS: emsg = ("Invalid application kind = '{0}' for RAET SaltEvent.".format(kind)) log.error(emsg + "\n") raise ValueError(emsg) if kind != self.node: emsg = ("Mismatch between node = '{0}' and kind = '{1}' in " "RAET SaltEvent.".format(self.node, kind)) log.error(emsg + '\n') raise ValueError(emsg) if self.node in [kinds.APPL_KIND_NAMES[kinds.applKinds.master], kinds.APPL_KIND_NAMES[kinds.applKinds.syndic]]: # []'master', 'syndic'] lanename = 'master' elif self.node in [kinds.APPL_KIND_NAMES[kinds.applKinds.minion], kinds.APPL_KIND_NAMES[kinds.applKinds.caller]]: # ['minion', 'caller'] role = self.opts.get('id', '') # opts required for minion if not role: emsg = ("Missing role required to setup RAET SaltEvent.") log.error(emsg + "\n") raise ValueError(emsg) if not kind: emsg = "Missing kind required to setup RAET SaltEvent." log.error(emsg + '\n') raise ValueError(emsg) lanename = "{0}_{1}".format(role, kind) else: emsg = ("Unsupported application node kind '{0}' for RAET SaltEvent.".format(self.node)) log.error(emsg + '\n') raise ValueError(emsg) name = 'event' + nacling.uuid(size=18) cachedir = self.opts.get('cachedir', os.path.join(syspaths.CACHE_DIR, self.node)) stack = LaneStack( name=name, lanename=lanename, sockdirpath=self.sock_dir) stack.Pk = raeting.PackKind.pack.value stack.addRemote(RemoteYard(stack=stack, lanename=lanename, name=ryn, dirpath=self.sock_dir)) return stack def subscribe(self, tag=None, match_type=None): ''' Included for compat with zeromq events, not required ''' if not self.connected: self.connect_pub() def unsubscribe(self, tag=None, match_type=None): ''' Included for compat with zeromq events, not required ''' return def connect_pub(self): ''' Establish the publish connection ''' try: route = {'dst': (None, self.ryn, 'event_req'), 'src': (None, self.stack.local.name, None)} msg = {'route': route} self.stack.transmit(msg, self.stack.nameRemotes[self.ryn].uid) self.stack.serviceAll() self.connected = True self.cpub = True except Exception: pass def connect_pull(self, timeout=1000): ''' Included for compat with zeromq events, not required ''' return @classmethod def unpack(cls, raw, serial=None): ''' Included for compat with zeromq events, not required ''' return raw def get_event(self, wait=5, tag='', match_type=None, full=False, no_block=None, auto_reconnect=False): ''' Get a single publication. IF no publication available THEN block for up to wait seconds AND either return publication OR None IF no publication available. IF wait is 0 then block forever. ''' if not self.connected: self.connect_pub() start = time.time() while True: self.stack.serviceAll() if self.stack.rxMsgs: msg, sender = self.stack.rxMsgs.popleft() if 'tag' not in msg and 'data' not in msg: # Invalid event, how did this get here? continue if not msg['tag'].startswith(tag) and self.cache_regex.get(tag).search(msg['tag']) is None: # Not what we are looking for, throw it away continue if full: return msg else: return msg['data'] if start + wait < time.time(): return None time.sleep(0.01) def get_event_noblock(self): ''' Get the raw event msg without blocking or any other niceties ''' if not self.connected: self.connect_pub() self.stack.serviceAll() if self.stack.rxMsgs: msg, sender = self.stack.rxMsgs.popleft() if 'tag' not in msg and 'data' not in msg: # Invalid event, how did this get here? return None return msg def iter_events(self, tag='', full=False, auto_reconnect=False): ''' Creates a generator that continuously listens for events ''' while True: data = self.get_event(tag=tag, full=full, auto_reconnect=auto_reconnect) if data is None: continue yield data def fire_event(self, data, tag, timeout=1000): ''' Send a single event into the publisher with paylod dict "data" and event identifier "tag" ''' # Timeout is retained for compat with zeromq events if not six.text_type(tag): # no empty tags allowed raise ValueError('Empty tag.') if not isinstance(data, MutableMapping): # data must be dict raise ValueError('Dict object expected, not \'{0}\'.'.format(data)) route = {'dst': (None, self.ryn, 'event_fire'), 'src': (None, self.stack.local.name, None)} msg = {'route': route, 'tag': tag, 'data': data} self.stack.transmit(msg, self.stack.nameRemotes[self.ryn].uid) self.stack.serviceAll() def fire_ret_load(self, load): ''' Fire events based on information in the return load ''' if load.get('retcode') and load.get('fun'): # Minion fired a bad retcode, fire an event if load['fun'] in salt.utils.event.SUB_EVENT: try: for tag, data in six.iteritems(load.get('return', {})): data['retcode'] = load['retcode'] tags = tag.split('_|-') if data.get('result') is False: self.fire_event( data, '{0}.{1}'.format(tags[0], tags[-1])) # old dup event data['jid'] = load['jid'] data['id'] = load['id'] data['success'] = False data['return'] = 'Error: {0}.{1}'.format(tags[0], tags[-1]) data['fun'] = load['fun'] data['user'] = load['user'] self.fire_event( data, salt.utils.event.tagify([load['jid'], 'sub', load['id'], 'error', load['fun']], 'job')) except Exception: pass def close_pub(self): ''' Here for compatability ''' return def destroy(self): if hasattr(self, 'stack'): self.stack.server.close() class MasterEvent(RAETEvent): ''' Create a master event management object ''' def __init__(self, opts, sock_dir, listen=True): super(MasterEvent, self).__init__('master', opts=opts, sock_dir=sock_dir, listen=listen) class PresenceEvent(MasterEvent): def __init__(self, opts, sock_dir, listen=True, state=None): self.state = state super(PresenceEvent, self).__init__(opts=opts, sock_dir=sock_dir, listen=listen) def connect_pub(self): ''' Establish the publish connection ''' try: route = {'dst': (None, self.ryn, 'presence_req'), 'src': (None, self.stack.local.name, None)} msg = {'route': route} if self.state: msg['data'] = {'state': self.state} self.stack.transmit(msg, self.stack.nameRemotes[self.ryn].uid) self.stack.serviceAll() self.connected = True except Exception: pass class StatsEvent(MasterEvent): def __init__(self, opts, sock_dir, tag, estate=None, listen=True): super(StatsEvent, self).__init__(opts=opts, sock_dir=sock_dir, listen=listen) self.tag = tag self.estate = estate def connect_pub(self): ''' Establish the publish connection ''' try: route = {'dst': (self.estate, None, 'stats_req'), 'src': (None, self.stack.local.name, None)} msg = {'route': route, 'tag': self.tag} self.stack.transmit(msg, self.stack.nameRemotes[self.ryn].uid) self.stack.serviceAll() self.connected = True except Exception: pass