Example #1
0
class RAETCaller(BaseCaller):
    '''
    Object to wrap the calling of local salt modules for the salt-call command
    when transport is raet

    There are two operation modes.
    1) Use a preexisting minion
    2) Set up a special caller minion if no preexisting minion
        The special caller minion is a subset whose only function is to perform
        Salt-calls with raet as the transport
        The essentials:
            A RoadStack whose local estate name is of the form "role_kind" where:
               role is the minion id opts['id']
               kind is opts['__role'] which should be 'caller' APPL_KIND_NAMES
               The RoadStack if for communication to/from a master

            A LaneStack with manor yard so that RaetChannels created by the func Jobbers
            can communicate through this manor yard then through the
            RoadStack to/from a master

            A Router to route between the stacks (Road and Lane)

            These are all managed via a FloScript named caller.flo

    '''
    def __init__(self, opts):
        '''
        Pass in the command line options
        '''
        self.process = None
        if not opts['local']:
            self.stack = self._setup_caller_stack(opts)
            salt.transport.jobber_stack = self.stack

            if (opts.get('__role') ==
                    kinds.APPL_KIND_NAMES[kinds.applKinds.caller]):
                # spin up and fork minion here
                self.process = MultiprocessingProcess(target=raet_minion_run,
                                    kwargs={'cleanup_protecteds': [self.stack.ha], })
                self.process.start()
                # wait here until '/var/run/salt/minion/alpha_caller.manor.uxd' exists
                self._wait_caller(opts)

        super(RAETCaller, self).__init__(opts)

    def run(self):
        '''
        Execute the salt call logic
        '''
        try:
            ret = self.call()
            if not self.opts['local']:
                self.stack.server.close()
                salt.transport.jobber_stack = None

            if self.opts['print_metadata']:
                print_ret = ret
            else:
                print_ret = ret.get('return', {})
            if self.process:
                self.process.terminate()
            salt.output.display_output(
                    {'local': print_ret},
                    ret.get('out', 'nested'),
                    self.opts)
            if self.opts.get('retcode_passthrough', False):
                sys.exit(ret['retcode'])

        except SaltInvocationError as err:
            raise SystemExit(err)

    def _setup_caller_stack(self, opts):
        '''
        Setup and return the LaneStack and Yard used by by channel when global
        not already setup such as in salt-call to communicate to-from the minion

        '''
        role = opts.get('id')
        if not role:
            emsg = ("Missing role required to setup RAETChannel.")
            log.error(emsg + "\n")
            raise ValueError(emsg)

        kind = opts.get('__role')  # application kind 'master', 'minion', etc
        if kind not in kinds.APPL_KINDS:
            emsg = ("Invalid application kind = '{0}' for RAETChannel.".format(kind))
            log.error(emsg + "\n")
            raise ValueError(emsg)
        if kind in [kinds.APPL_KIND_NAMES[kinds.applKinds.minion],
                    kinds.APPL_KIND_NAMES[kinds.applKinds.caller], ]:
            lanename = "{0}_{1}".format(role, kind)
        else:
            emsg = ("Unsupported application kind '{0}' for RAETChannel.".format(kind))
            log.error(emsg + '\n')
            raise ValueError(emsg)

        sockdirpath = opts['sock_dir']
        stackname = 'caller' + nacling.uuid(size=18)
        stack = LaneStack(name=stackname,
                          lanename=lanename,
                          sockdirpath=sockdirpath)

        stack.Pk = raeting.PackKind.pack.value
        stack.addRemote(RemoteYard(stack=stack,
                                   name='manor',
                                   lanename=lanename,
                                   dirpath=sockdirpath))
        log.debug("Created Caller Jobber Stack {0}\n".format(stack.name))

        return stack

    def _wait_caller(self, opts):
        '''
        Returns when RAET Minion Yard is available
        '''
        yardname = 'manor'
        dirpath = opts['sock_dir']

        role = opts.get('id')
        if not role:
            emsg = ("Missing role required to setup RAET SaltCaller.")
            log.error(emsg + "\n")
            raise ValueError(emsg)

        kind = opts.get('__role')  # application kind 'master', 'minion', etc
        if kind not in kinds.APPL_KINDS:
            emsg = ("Invalid application kind = '{0}' for RAET SaltCaller.".format(kind))
            log.error(emsg + "\n")
            raise ValueError(emsg)

        if kind in [kinds.APPL_KIND_NAMES[kinds.applKinds.minion],
                    kinds.APPL_KIND_NAMES[kinds.applKinds.caller], ]:
            lanename = "{0}_{1}".format(role, kind)
        else:
            emsg = ("Unsupported application kind '{0}' for RAET SaltCaller.".format(kind))
            log.error(emsg + '\n')
            raise ValueError(emsg)

        ha, dirpath = Yard.computeHa(dirpath, lanename, yardname)

        if is_windows():
            # RAET lanes do not use files on Windows. Need to use win32file
            # API to check for existence.
            exists = False
            while not exists:
                try:
                    f = win32file.CreateFile(
                            ha,
                            win32file.GENERIC_WRITE | win32file.GENERIC_READ,
                            win32file.FILE_SHARE_READ,
                            None,
                            win32file.OPEN_EXISTING,
                            0,
                            None)
                    win32file.CloseHandle(f)
                    exists = True
                except win32file.error:
                    time.sleep(0.1)
        else:
            while not ((os.path.exists(ha) and
                        not os.path.isfile(ha) and
                        not os.path.isdir(ha))):
                time.sleep(0.1)
        time.sleep(0.5)
Example #2
0
    def handle_ssh(self, mine=False):
        '''
        Spin up the needed threads or processes and execute the subsequent
        routines
        '''
        que = multiprocessing.Queue()
        running = {}
        target_iter = self.targets.__iter__()
        returned = set()
        rets = set()
        init = False
        while True:
            if not self.targets:
                log.error('No matching targets found in roster.')
                break
            if len(running) < self.opts.get('ssh_max_procs', 25) and not init:
                try:
                    host = next(target_iter)
                except StopIteration:
                    init = True
                    continue
                for default in self.defaults:
                    if default not in self.targets[host]:
                        self.targets[host][default] = self.defaults[default]
                args = (
                        que,
                        self.opts,
                        host,
                        self.targets[host],
                        mine,
                        )
                routine = MultiprocessingProcess(
                                target=self.handle_routine,
                                args=args)
                routine.start()
                running[host] = {'thread': routine}
                continue
            ret = {}
            try:
                ret = que.get(False)
                if 'id' in ret:
                    returned.add(ret['id'])
                    yield {ret['id']: ret['ret']}
            except Exception:
                # This bare exception is here to catch spurious exceptions
                # thrown by que.get during healthy operation. Please do not
                # worry about this bare exception, it is entirely here to
                # control program flow.
                pass
            for host in running:
                if not running[host]['thread'].is_alive():
                    if host not in returned:
                        # Try to get any returns that came through since we
                        # last checked
                        try:
                            while True:
                                ret = que.get(False)
                                if 'id' in ret:
                                    returned.add(ret['id'])
                                    yield {ret['id']: ret['ret']}
                        except Exception:
                            pass

                        if host not in returned:
                            error = ('Target \'{0}\' did not return any data, '
                                     'probably due to an error.').format(host)
                            ret = {'id': host,
                                   'ret': error}
                            log.error(error)
                            yield {ret['id']: ret['ret']}
                    running[host]['thread'].join()
                    rets.add(host)
            for host in rets:
                if host in running:
                    running.pop(host)
            if len(rets) >= len(self.targets):
                break
            # Sleep when limit or all threads started
            if len(running) >= self.opts.get('ssh_max_procs', 25) or len(self.targets) >= len(running):
                time.sleep(0.1)
Example #3
0
    def handle_ssh(self, mine=False):
        '''
        Spin up the needed threads or processes and execute the subsequent
        routines
        '''
        que = multiprocessing.Queue()
        running = {}
        target_iter = self.targets.__iter__()
        returned = set()
        rets = set()
        init = False
        while True:
            if not self.targets:
                log.error('No matching targets found in roster.')
                break
            if len(running) < self.opts.get('ssh_max_procs', 25) and not init:
                try:
                    host = next(target_iter)
                except StopIteration:
                    init = True
                    continue
                for default in self.defaults:
                    if default not in self.targets[host]:
                        self.targets[host][default] = self.defaults[default]
                args = (
                        que,
                        self.opts,
                        host,
                        self.targets[host],
                        mine,
                        )
                routine = MultiprocessingProcess(
                                target=self.handle_routine,
                                args=args)
                routine.start()
                running[host] = {'thread': routine}
                continue
            ret = {}
            try:
                ret = que.get(False)
                if 'id' in ret:
                    returned.add(ret['id'])
                    yield {ret['id']: ret['ret']}
            except Exception:
                # This bare exception is here to catch spurious exceptions
                # thrown by que.get during healthy operation. Please do not
                # worry about this bare exception, it is entirely here to
                # control program flow.
                pass
            for host in running:
                if not running[host]['thread'].is_alive():
                    if host not in returned:
                        # Try to get any returns that came through since we
                        # last checked
                        try:
                            while True:
                                ret = que.get(False)
                                if 'id' in ret:
                                    returned.add(ret['id'])
                                    yield {ret['id']: ret['ret']}
                        except Exception:
                            pass

                        if host not in returned:
                            error = ('Target \'{0}\' did not return any data, '
                                     'probably due to an error.').format(host)
                            ret = {'id': host,
                                   'ret': error}
                            log.error(error)
                            yield {ret['id']: ret['ret']}
                    running[host]['thread'].join()
                    rets.add(host)
            for host in rets:
                if host in running:
                    running.pop(host)
            if len(rets) >= len(self.targets):
                break
            # Sleep when limit or all threads started
            if len(running) >= self.opts.get('ssh_max_procs', 25) or len(self.targets) >= len(running):
                time.sleep(0.1)
Example #4
0
class RAETCaller(BaseCaller):
    '''
    Object to wrap the calling of local salt modules for the salt-call command
    when transport is raet

    There are two operation modes.
    1) Use a preexisting minion
    2) Set up a special caller minion if no preexisting minion
        The special caller minion is a subset whose only function is to perform
        Salt-calls with raet as the transport
        The essentials:
            A RoadStack whose local estate name is of the form "role_kind" where:
               role is the minion id opts['id']
               kind is opts['__role'] which should be 'caller' APPL_KIND_NAMES
               The RoadStack if for communication to/from a master

            A LaneStack with manor yard so that RaetChannels created by the func Jobbers
            can communicate through this manor yard then through the
            RoadStack to/from a master

            A Router to route between the stacks (Road and Lane)

            These are all managed via a FloScript named caller.flo

    '''
    def __init__(self, opts):
        '''
        Pass in the command line options
        '''
        self.process = None
        if not opts['local']:
            self.stack = self._setup_caller_stack(opts)
            salt.transport.jobber_stack = self.stack

            if (opts.get('__role') ==
                    kinds.APPL_KIND_NAMES[kinds.applKinds.caller]):
                # spin up and fork minion here
                self.process = MultiprocessingProcess(target=raet_minion_run,
                                    kwargs={'cleanup_protecteds': [self.stack.ha], })
                self.process.start()
                # wait here until '/var/run/salt/minion/alpha_caller.manor.uxd' exists
                self._wait_caller(opts)

        super(RAETCaller, self).__init__(opts)

    def run(self):
        '''
        Execute the salt call logic
        '''
        try:
            ret = self.call()
            if not self.opts['local']:
                self.stack.server.close()
                salt.transport.jobber_stack = None

            if self.opts['metadata']:
                print_ret = ret
            else:
                print_ret = ret.get('return', {})
            if self.process:
                self.process.terminate()
            salt.output.display_output(
                    {'local': print_ret},
                    ret.get('out', 'nested'),
                    self.opts)
            if self.opts.get('retcode_passthrough', False):
                sys.exit(ret['retcode'])

        except SaltInvocationError as err:
            raise SystemExit(err)

    def _setup_caller_stack(self, opts):
        '''
        Setup and return the LaneStack and Yard used by by channel when global
        not already setup such as in salt-call to communicate to-from the minion

        '''
        role = opts.get('id')
        if not role:
            emsg = ("Missing role required to setup RAETChannel.")
            log.error(emsg + "\n")
            raise ValueError(emsg)

        kind = opts.get('__role')  # application kind 'master', 'minion', etc
        if kind not in kinds.APPL_KINDS:
            emsg = ("Invalid application kind = '{0}' for RAETChannel.".format(kind))
            log.error(emsg + "\n")
            raise ValueError(emsg)
        if kind in [kinds.APPL_KIND_NAMES[kinds.applKinds.minion],
                    kinds.APPL_KIND_NAMES[kinds.applKinds.caller], ]:
            lanename = "{0}_{1}".format(role, kind)
        else:
            emsg = ("Unsupported application kind '{0}' for RAETChannel.".format(kind))
            log.error(emsg + '\n')
            raise ValueError(emsg)

        sockdirpath = opts['sock_dir']
        stackname = 'caller' + nacling.uuid(size=18)
        stack = LaneStack(name=stackname,
                          lanename=lanename,
                          sockdirpath=sockdirpath)

        stack.Pk = raeting.PackKind.pack.value
        stack.addRemote(RemoteYard(stack=stack,
                                   name='manor',
                                   lanename=lanename,
                                   dirpath=sockdirpath))
        log.debug("Created Caller Jobber Stack {0}\n".format(stack.name))

        return stack

    def _wait_caller(self, opts):
        '''
        Returns when RAET Minion Yard is available
        '''
        yardname = 'manor'
        dirpath = opts['sock_dir']

        role = opts.get('id')
        if not role:
            emsg = ("Missing role required to setup RAET SaltCaller.")
            log.error(emsg + "\n")
            raise ValueError(emsg)

        kind = opts.get('__role')  # application kind 'master', 'minion', etc
        if kind not in kinds.APPL_KINDS:
            emsg = ("Invalid application kind = '{0}' for RAET SaltCaller.".format(kind))
            log.error(emsg + "\n")
            raise ValueError(emsg)

        if kind in [kinds.APPL_KIND_NAMES[kinds.applKinds.minion],
                    kinds.APPL_KIND_NAMES[kinds.applKinds.caller], ]:
            lanename = "{0}_{1}".format(role, kind)
        else:
            emsg = ("Unsupported application kind '{0}' for RAET SaltCaller.".format(kind))
            log.error(emsg + '\n')
            raise ValueError(emsg)

        ha, dirpath = Yard.computeHa(dirpath, lanename, yardname)

        if is_windows():
            # RAET lanes do not use files on Windows. Need to use win32file
            # API to check for existence.
            exists = False
            while not exists:
                try:
                    f = win32file.CreateFile(
                            ha,
                            win32file.GENERIC_WRITE | win32file.GENERIC_READ,
                            win32file.FILE_SHARE_READ,
                            None,
                            win32file.OPEN_EXISTING,
                            0,
                            None)
                    win32file.CloseHandle(f)
                    exists = True
                except win32file.error:
                    time.sleep(0.1)
        else:
            while not ((os.path.exists(ha) and
                        not os.path.isfile(ha) and
                        not os.path.isdir(ha))):
                time.sleep(0.1)
        time.sleep(0.5)