Beispiel #1
0
class FSProxyAction(CommonAction):
    """
    Generic file system command proxy action class.
    """

    NAME = 'proxy'

    def __init__(self, fs, action, nodes, debug, comps=None, **kwargs):

        CommonAction.__init__(self)

        self.progpath = os.path.abspath(sys.argv[0])
        self.fs = fs
        self.action = action
        self.nodes = nodes
        self.debug = debug

        self._comps = comps

        self.options = {}
        for optname in ('addopts', 'failover', 'mountdata', 'fanout',
                        'dryrun'):
            self.options[optname] = kwargs.get(optname)

        self._outputs = MsgTree()
        self._errpickle = MsgTree()
        self._silentnodes = NodeSet()  # Error nodes without output

        if self.fs.debug:
            print("FSProxyAction %s on %s" % (action, nodes))

    def info(self):
        return ActionInfo(self, description='Proxy action')

    def _prepare_cmd(self):
        """Create the command line base on proxy properties."""

        command = ["%s" % self.progpath]
        command.append(self.action)
        command.append("-f %s" % self.fs.fs_name)
        command.append("-R")

        if self.debug:
            command.append("-d")

        if self._comps:
            command.append("-l %s" % self._comps.labels())

        if self.options['addopts']:
            command.append("-o '%s'" % self.options['addopts'])

        if self.options['failover']:
            command.append("-F '%s'" % self.options['failover'])

        if self.options['fanout'] is not None:
            command.append('--fanout=%d' % self.options['fanout'])

        if self.options['dryrun']:
            command.append('--dry-run')

        # To be compatible with older clients in most cases, do not set the
        # option when it is its default value.
        if self.options['mountdata'] not in (None, 'auto'):
            command.append('--mountdata=%s' % self.options['mountdata'])

        return command

    def _launch(self):
        """Launch FS proxy command."""
        command = self._prepare_cmd()

        # Schedule cluster command.
        self.task.shell(' '.join(command), nodes=self.nodes, handler=self)

        # Launch events
        self._actions_start()

    def _actions_start(self):
        """
        Raise 'proxy' events for all components related to this ProxyAction.
        """
        # Add a 'proxy' running action for each component.
        if self._comps:
            for comp in self._comps:
                # This special event is raised to keep track of undergoing
                # actions. Maybe this could be dropped is such tracking is no
                # more needed.
                comp.action_event(self, 'start')

    def ev_read(self, worker):
        node = worker.current_node
        buf = worker.current_msg
        try:
            data = shine_msg_unpack(buf)

            # COMPAT: Prior to 1.4, 'comp'+'action' was used.
            # 1.4+ uses ActionInfo
            if 'comp' in data:
                action = Action()
                action.NAME = data.pop('action')
                comp = data.pop('comp')
                comp.fs = self.fs
                desc = "%s of %s" % (action.NAME, comp.longtext())
                data['info'] = ActionInfo(action, comp, desc)
                evtype = 'comp'
            else:
                evtype = data.pop('evtype')

            self.fs.distant_event(evtype, node=node, **data)
        except ProxyActionUnpickleError as exp:
            # Maintain a standalone list of unpickling errors.
            # Node could have unpickling error but still exit with 0
            msg = str(exp)
            if msg not in self._errpickle.get(node, ""):
                self._errpickle.add(node, msg)
        except AttributeError as exp:
            msg = "Cannot read message (check Shine and ClusterShell " \
                  "version): %s" % str(exp)
            if msg not in self._errpickle.get(node, ""):
                self._errpickle.add(node, msg)
        except ProxyActionUnpackError:
            # Store output that is not a shine message
            self._outputs.add(node, buf)

    def ev_hup(self, worker):
        """Keep a list of node, without output, with a return code != 0"""
        # If this node was on error
        if worker.current_rc != 0:
            # If there is no known outputs
            if self._outputs.get(worker.current_node) is None:
                self._silentnodes.add(worker.current_node)

    def ev_close(self, worker):
        """End of proxy command."""
        Action.ev_close(self, worker)

        # Before all, we must check if shine command ran without bugs, node
        # crash, etc...
        # So we need to verify all node retcodes and change the component state
        # on the bad nodes.

        # Action timed out
        if worker.did_timeout():
            self.set_status(ACT_ERROR)
            return

        status = ACT_OK

        # Remove the 'proxy' running action for each component.
        if self._comps:
            for comp in self._comps:
                # This special event helps to keep track of undergoing actions
                # (see ev_start())
                comp.action_event(self, 'done')
                comp.sanitize_state(nodes=worker.nodes)

        # Gather nodes by return code
        for rc, nodes in worker.iter_retcodes():
            # Remote command returns only RUNTIME_ERROR (See RemoteCommand)
            # some common remote errors:
            # rc 127 = command not found
            # rc 126 = found but not executable
            # rc 1 = python failure...
            if rc != 0:

                # If there is at least one error, the action is on error.
                status = ACT_ERROR

                # Gather these nodes by buffer
                key = nodes.__contains__
                for buffers, nodes in self._outputs.walk(match=key):
                    # Handle proxy command error
                    nodes = NodeSet.fromlist(nodes)
                    msg = "Remote action %s failed: %s\n" % \
                                                        (self.action, buffers)
                    self.fs._handle_shine_proxy_error(nodes, msg)

        # Raise errors for each unpickling error,
        # which could happen mostly when Shine exits with 0.
        for buffers, nodes in self._errpickle.walk():
            nodes = NodeSet.fromlist(nodes)
            self.fs._handle_shine_proxy_error(nodes, str(buffers))

        # Raise an error for nodes without output
        if len(self._silentnodes) > 0:
            msg = "Remote action %s failed: No response" % self.action
            self.fs._handle_shine_proxy_error(self._silentnodes, msg)

        self.set_status(status)
Beispiel #2
0
class FSProxyAction(CommonAction):
    """
    Generic file system command proxy action class.
    """

    NAME = 'proxy'

    def __init__(self, fs, action, nodes, debug, comps=None, **kwargs):

        CommonAction.__init__(self)

        self.progpath = os.path.abspath(sys.argv[0])
        self.fs = fs
        self.action = action
        self.nodes = nodes
        self.debug = debug

        self._comps = comps

        self.options = {}
        for optname in ('addopts', 'failover', 'mountdata', 'fanout',
                        'dryrun'):
            self.options[optname] = kwargs.get(optname)

        self._outputs = MsgTree()
        self._errpickle = MsgTree()
        self._silentnodes = NodeSet()  # Error nodes without output

        if self.fs.debug:
            print "FSProxyAction %s on %s" % (action, nodes)

    def info(self):
        return ActionInfo(self, description='Proxy action')

    def _prepare_cmd(self):
        """Create the command line base on proxy properties."""

        command = ["%s" % self.progpath]
        command.append(self.action)
        command.append("-f %s" % self.fs.fs_name)
        command.append("-R")

        if self.debug:
            command.append("-d")

        if self._comps:
            command.append("-l %s" % self._comps.labels())

        if self.options['addopts']:
            command.append("-o '%s'" % self.options['addopts'])

        if self.options['failover']:
            command.append("-F '%s'" % self.options['failover'])

        if self.options['fanout'] is not None:
            command.append('--fanout=%d' % self.options['fanout'])

        if self.options['dryrun']:
            command.append('--dry-run')

        # To be compatible with older clients in most cases, do not set the
        # option when it is its default value.
        if self.options['mountdata'] not in (None, 'auto'):
            command.append('--mountdata=%s' % self.options['mountdata'])

        return command

    def _launch(self):
        """Launch FS proxy command."""
        command = self._prepare_cmd()

        # Schedule cluster command.
        self.task.shell(' '.join(command), nodes=self.nodes, handler=self)

        # Launch events
        self._actions_start()

    def _actions_start(self):
        """
        Raise 'proxy' events for all components related to this ProxyAction.
        """
        # Add a 'proxy' running action for each component.
        if self._comps:
            for comp in self._comps:
                # This special event is raised to keep track of undergoing
                # actions. Maybe this could be dropped is such tracking is no
                # more needed.
                comp.action_event(self, 'start')

    def ev_read(self, worker):
        node = worker.current_node
        buf = worker.current_msg
        try:
            data = shine_msg_unpack(buf)

            # COMPAT: Prior to 1.4, 'comp'+'action' was used.
            # 1.4+ uses ActionInfo
            if 'comp' in data:
                action = Action()
                action.NAME = data.pop('action')
                comp = data.pop('comp')
                comp.fs = self.fs
                desc = "%s of %s" % (action.NAME, comp.longtext())
                data['info'] = ActionInfo(action, comp, desc)
                evtype = 'comp'
            else:
                evtype = data.pop('evtype')

            self.fs.distant_event(evtype, node=node, **data)
        except ProxyActionUnpickleError, exp:
            # Maintain a standalone list of unpickling errors.
            # Node could have unpickling error but still exit with 0
            msg = str(exp)
            if msg not in self._errpickle.get(node, ""):
                self._errpickle.add(node, msg)
        except AttributeError, exp:
            msg = "Cannot read message (check Shine and ClusterShell " \
                  "version): %s" % str(exp)
            if msg not in self._errpickle.get(node, ""):
                self._errpickle.add(node, msg)
Beispiel #3
0
class FSProxyAction(CommonAction):
    """
    Generic file system command proxy action class.
    """

    NAME = 'proxy'

    def __init__(self, fs, action, nodes, debug, comps=None, addopts=None,
                 failover=None, mountdata=None):

        CommonAction.__init__(self)

        self.progpath = os.path.abspath(sys.argv[0])
        self.fs = fs
        self.action = action
        self.nodes = nodes
        self.debug = debug

        self._comps = comps

        self.addopts = addopts
        self.failover = failover
        self.mountdata = mountdata

        self._outputs = MsgTree()
        self._errpickle = MsgTree()
        self._silentnodes = NodeSet() # Error nodes without output

        if self.fs.debug:
            print "FSProxyAction %s on %s" % (action, nodes)

    def _prepare_cmd(self):
        """Create the command line base on proxy properties."""

        command = ["%s" % self.progpath]
        command.append(self.action)
        command.append("-f %s" % self.fs.fs_name)
        command.append("-R")

        if self.debug:
            command.append("-d")

        if self._comps:
            command.append("-l %s" % self._comps.labels())

        if self.addopts:
            command.append("-o '%s'" % self.addopts)

        if self.failover:
            command.append("-F '%s'" % self.failover)

        # To be compatible with older clients in most cases, do not set the
        # option when it is its default value.
        if self.mountdata is not None and self.mountdata != 'auto':
            command.append('--mountdata=%s' % self.mountdata)

        return command

    def _launch(self):
        """Launch FS proxy command."""
        command = self._prepare_cmd()

        # Schedule cluster command.
        self.task.shell(' '.join(command), nodes=self.nodes, handler=self)

        # Launch events
        self._actions_start()

    def _actions_start(self):
        """
        Raise 'proxy' events for all components related to this ProxyAction.
        """
        # Add a 'proxy' running action for each component.
        if self._comps:
            for comp in self._comps:
                # Warning: there is no clean call at the end of the action.
                # cleaning is done by hand.
                comp.action_start('proxy')

    def ev_read(self, worker):
        node = worker.current_node
        buf = worker.current_msg
        try:
            data = shine_msg_unpack(buf)
            compname = data.pop('compname')
            action = data.pop('action')
            status = data.pop('status')
            self.fs.distant_event(compname, action, status, node=node, **data)
        except ProxyActionUnpickleError, exp:
            # Maintain a standalone list of unpickling errors.
            # Node could have unpickling error but still exit with 0
            msg = str(exp)
            if msg not in self._errpickle.get(node, ""):
                self._errpickle.add(node, msg)
        except AttributeError, exp:
            msg = "Cannot read message (check Shine and ClusterShell " \
                  "version): %s" % str(exp)
            if msg not in self._errpickle.get(node, ""):
                self._errpickle.add(node, msg)