コード例 #1
0
ファイル: actor.py プロジェクト: vmillnert/calvin-base
class Actor(object):
    """
    Base class for all actors
    Need a name supplied.
    Subclasses need to declare the parameter
    calvinsys if they want access to system
    interface on the node, this parameter
    will be supplied by the node and not by user
    """

    # Class variable controls action priority order
    action_priority = tuple()

    # Internal state (status)
    class FSM(object):
        def __init__(self,
                     states,
                     initial,
                     transitions,
                     hooks=None,
                     allow_invalid_transitions=True,
                     disable_transition_checks=False,
                     disable_state_checks=False):
            self.states = states
            self._state = initial
            self.transitions = transitions
            self.hooks = hooks or {}
            self.allow_invalid_transitions = allow_invalid_transitions
            self.disable_transition_checks = disable_transition_checks
            # disable_state_checks is used in the verify_status decorator
            self.disable_state_checks = disable_state_checks

        def state(self):
            return self._state

        def transition_to(self, new_state):
            if new_state in self.transitions[
                    self._state] or self.disable_transition_checks:
                hook = self.hooks.get((self._state, new_state), None)
                if hook:
                    hook()
                self._state = new_state
            else:
                msg = "Invalid transition %s -> %s" % (
                    self, self.printable(new_state))
                if self.allow_invalid_transitions:
                    _log.warning("ALLOWING " + msg)
                    self._state = new_state
                else:
                    raise Exception(msg)

        def printable(self, state):
            return self.states.reverse_mapping[state]

        def __str__(self):
            return self.printable(self._state)

    STATUS = enum('LOADED', 'READY', 'PENDING', 'ENABLED', 'DENIED',
                  'MIGRATABLE')

    VALID_TRANSITIONS = {
        STATUS.LOADED: [STATUS.READY],
        STATUS.READY: [STATUS.PENDING, STATUS.ENABLED, STATUS.DENIED],
        STATUS.PENDING: [STATUS.READY, STATUS.PENDING, STATUS.ENABLED],
        STATUS.ENABLED: [STATUS.READY, STATUS.PENDING, STATUS.DENIED],
        STATUS.DENIED: [STATUS.ENABLED, STATUS.MIGRATABLE, STATUS.PENDING],
        STATUS.MIGRATABLE: [STATUS.READY, STATUS.DENIED]
    }

    test_args = ()
    test_kwargs = {}

    @property
    def id(self):
        return self._id

    @property
    def name(self):
        return self._name

    @name.setter
    def name(self, value):
        self._name = value

    @property
    def migration_info(self):
        return self._migration_info

    # What are the arguments, really?
    def __init__(self,
                 actor_type,
                 name='',
                 allow_invalid_transitions=True,
                 disable_transition_checks=False,
                 disable_state_checks=False,
                 actor_id=None,
                 security=None):
        """Should _not_ be overridden in subclasses."""
        super(Actor, self).__init__()
        self._type = actor_type
        self._name = name  # optional: human_readable_name
        self._id = actor_id or calvinuuid.uuid("ACTOR")
        _log.debug("New actor id: %s, supplied actor id %s" %
                   (self._id, actor_id))
        self._deployment_requirements = []
        self._port_property_capabilities = None
        self._signature = None
        self._component_members = set(
            [self._id])  # We are only part of component if this is extended
        self._managed = set(
            ('_id', '_name', '_has_started', '_deployment_requirements',
             '_signature', '_subject_attributes', '_migration_info',
             "_port_property_capabilities", "_replication_data"))
        self._has_started = False
        self._calvinsys = None
        self.calvinsys = None
        self._using = {}
        # self.control = calvincontrol.get_calvincontrol()
        # self.metering = metering.get_metering()
        self._migration_info = None
        self._migrating_to = None  # During migration while on the previous node set to the next node id
        self._last_time_warning = 0.0
        self.sec = security
        self._subject_attributes = self.sec.get_subject_attributes(
        ) if self.sec is not None else None
        self.authorization_checks = None
        self._replication_data = ReplicationData(initialize=False)
        self._exhaust_cb = None

        self.inports = {
            p: actorport.InPort(p, self, pp)
            for p, pp in self.inport_properties.items()
        }
        self.outports = {
            p: actorport.OutPort(p, self, pp)
            for p, pp in self.outport_properties.items()
        }

        hooks = {
            (Actor.STATUS.PENDING, Actor.STATUS.ENABLED): self._will_start,
            (Actor.STATUS.ENABLED, Actor.STATUS.PENDING): self.will_stop,
        }
        self.fsm = Actor.FSM(
            Actor.STATUS,
            Actor.STATUS.LOADED,
            Actor.VALID_TRANSITIONS,
            hooks,
            allow_invalid_transitions=allow_invalid_transitions,
            disable_transition_checks=disable_transition_checks,
            disable_state_checks=disable_state_checks)
        # self.metering.add_actor_info(self)

    def set_authorization_checks(self, authorization_checks):
        self.authorization_checks = authorization_checks

    @verify_status([STATUS.LOADED])
    def setup_complete(self):
        self.fsm.transition_to(Actor.STATUS.READY)

    def init(self):
        raise Exception("Implementing 'init()' is mandatory.")

    def _will_start(self):
        """Ensure will_start() is only called once"""
        if not self._has_started:
            self.will_start()
            self._has_started = True

    def will_start(self):
        """Override in actor subclass if actions need to be taken before starting."""
        pass

    def will_stop(self):
        """Override in actor subclass if actions need to be taken before stopping."""
        pass

    def will_migrate(self):
        """Override in actor subclass if actions need to be taken before migrating."""
        pass

    def did_migrate(self):
        """Override in actor subclass if actions need to be taken after migrating."""
        pass

    def will_end(self):
        """Override in actor subclass if actions need to be taken before destruction."""
        pass

    def will_replicate(self, state):
        """Override in actor subclass if actions need to be taken before replication."""
        pass

    def __getitem__(self, attr):
        if attr in self._using:
            return self._using[attr]
        raise KeyError(attr)

    def use(self, requirement, shorthand):
        self._using[shorthand] = self._calvinsys.use_requirement(
            self, requirement)

    def __str__(self):
        ip = ""
        for p in self.inports.values():
            ip = ip + str(p)
        op = ""
        for p in self.outports.values():
            op = op + str(p)
        s = "Actor: '%s' class '%s'\nstatus: %s\ninports: %s\noutports:%s" % (
            self._name, self._type, self.fsm, ip, op)
        return s

    @verify_status([STATUS.READY, STATUS.PENDING, STATUS.ENABLED])
    def did_connect(self, port):
        """Called when a port is connected, checks actor is fully connected."""
        if self.fsm.state() == Actor.STATUS.ENABLED:
            # We already was enabled thats fine now with dynamic port connections
            return
        _log.debug("actor.did_connect BEGIN %s %s " % (self._name, self._id))
        # If we happen to be in READY, go to PENDING
        if self.fsm.state() == Actor.STATUS.READY:
            self.fsm.transition_to(Actor.STATUS.PENDING)
        # Three non-patological options:
        # have inports, have outports, or have in- and outports

        if self.inports:
            for p in self.inports.values():
                if not p.is_connected():
                    return

        if self.outports:
            for p in self.outports.values():
                if not p.is_connected():
                    return

        # If we made it here, all ports are connected
        self.fsm.transition_to(Actor.STATUS.ENABLED)
        _log.debug("actor.did_connect ENABLED %s %s " % (self._name, self._id))

        # Actor enabled, inform scheduler
        self._calvinsys.scheduler_wakeup()

    @verify_status(
        [STATUS.ENABLED, STATUS.PENDING, STATUS.DENIED, STATUS.MIGRATABLE])
    def did_disconnect(self, port):
        """Called when a port is disconnected, checks actor is fully disconnected."""
        # If the actor is MIGRATABLE, return since it will be migrated soon.
        _log.debug("Actor %s did_disconnect %s" %
                   (self._id, Actor.STATUS.reverse_mapping[self.fsm.state()]))
        if self.fsm.state() == Actor.STATUS.MIGRATABLE:
            return
        # If we happen to be in ENABLED/DENIED, go to PENDING
        if self.fsm.state() != Actor.STATUS.PENDING:
            self.fsm.transition_to(Actor.STATUS.PENDING)

        # Three non-patological options:
        # have inports, have outports, or have in- and outports
        if self.inports:
            for p in self.inports.values():
                if p.is_connected():
                    return

        if self.outports:
            for p in self.outports.values():
                if p.is_connected():
                    return

        # If we made it here, all ports are disconnected
        self.fsm.transition_to(Actor.STATUS.READY)

    def exhaust(self, callback):
        self._exhaust_cb = callback

    def get_pressure(self):
        pressure = {}
        for port in self.inports.values():
            for e in port.endpoints:
                PRESSURE_LENGTH = len(e.pressure)
                pressure[(port.id,
                          e.peer_id)] = (e.pressure_last, e.pressure_count, [
                              e.pressure[t % PRESSURE_LENGTH] for t in range(
                                  max(0, e.pressure_count -
                                      PRESSURE_LENGTH), e.pressure_count)
                          ])
        return pressure

    #
    # FIXME: The following methods (_authorized, _warn_slow_actor, _handle_exhaustion) were
    #        extracted from fire() to make the logic easier to follow
    #
    def _authorized(self):
        authorized = self.check_authorization_decision()
        if not authorized:
            _log.info("Access denied for actor %s(%s)" %
                      (self._type, self._id))
            # The authorization decision is not valid anymore.
            # Change actor status to DENIED.
            self.fsm.transition_to(Actor.STATUS.DENIED)
            # Try to migrate actor.
            self.sec.authorization_runtime_search(self._id,
                                                  self._signature,
                                                  callback=CalvinCB(
                                                      self.set_migration_info))
        return authorized

    def _warn_slow_actor(self, time_spent, start_time):
        time_since_warning = start_time - self._last_time_warning
        if time_since_warning < 120.0:
            return
        self._last_time_warning = start_time
        _log.warning("%s (%s) actor blocked for %f sec" %
                     (self._name, self._type, time_spent))

    def _handle_exhaustion(self, exhausted_ports, output_ok):
        _log.debug("actor_fire %s test exhaust %s, %s, %s" %
                   (self._id, self._exhaust_cb
                    is not None, exhausted_ports, output_ok))
        for port in exhausted_ports:
            # Might result in actor changing to PENDING
            try:
                port.finished_exhaustion()
            except:
                _log.exception("FINSIHED EXHAUSTION FAILED")
        if (output_ok and self._exhaust_cb is not None and not any([
                p.any_outstanding_exhaustion_tokens()
                for p in self.inports.values()
        ])):
            _log.debug("actor %s exhausted" % self._id)
            # We are in exhaustion, got all exhaustion tokens from peer ports
            # but stopped firing while outport token slots available, i.e. exhausted inports or deadlock
            # FIXME handle exhaustion deadlock
            # Initiate disconnect of outports and destroy the actor
            async .DelayedCall(0,
                               self._exhaust_cb,
                               status=response.CalvinResponse(True))
            self._exhaust_cb = None

    @verify_status([STATUS.ENABLED])
    def fire(self):
        """
        Fire an actor.
        Returns True if any action fired
        """
        # FIXME: Move authorization decision to scheduler
        #
        # First make sure we are allowed to run
        #
        if not self._authorized():
            return False

        start_time = time.time()
        actor_did_fire = False
        #
        # Repeatedly go over the action priority list
        #
        done = False
        while not done:
            for action_method in self.__class__.action_priority:
                did_fire, output_ok, exhausted = action_method(self)
                actor_did_fire |= did_fire
                # Action firing should fire the first action that can fire,
                # hence when fired start from the beginning priority list
                if did_fire:
                    # # FIXME: Add hooks for metering and probing
                    # self.metering.fired(self._id, action_method.__name__)
                    # self.control.log_actor_firing( ... )
                    break

            #
            # We end up here when an action fired or when all actions have failed to fire
            #
            if did_fire:
                #
                # Limit time given to actors even if it could continue a new round of firing
                #
                # FIXME: IMHO this decision should be made in the scheduler. No timing here.
                time_spent = time.time() - start_time
                done = time_spent > 0.020
            else:
                #
                # We reached the end of the list without ANY firing during this round
                # => handle exhaustion and return
                #
                # FIXME: Move exhaust handling to scheduler
                self._handle_exhaustion(exhausted, output_ok)
                done = True

        return actor_did_fire

    def enabled(self):
        # We want to run even if not fully connected during exhaustion
        r = self.fsm.state(
        ) == Actor.STATUS.ENABLED or self._exhaust_cb is not None
        if not r:
            _log.debug("Actor %s %s not enabled" % (self._name, self._id))
        return r

    def denied(self):
        return self.fsm.state() == Actor.STATUS.DENIED

    def migratable(self):
        return self.fsm.state() == Actor.STATUS.MIGRATABLE

    @verify_status([STATUS.DENIED])
    def enable_or_migrate(self):
        """Enable actor if access is permitted. Try to migrate if access still denied."""
        if self.check_authorization_decision():
            self.fsm.transition_to(Actor.STATUS.ENABLED)
        else:
            # Try to migrate actor.
            self.sec.authorization_runtime_search(self._id,
                                                  self._signature,
                                                  callback=CalvinCB(
                                                      self.set_migration_info))

    # DEPRECATED: Only here for backwards compatibility
    @verify_status([STATUS.ENABLED])
    def enable(self):
        self.fsm.transition_to(Actor.STATUS.ENABLED)

    @verify_status([STATUS.READY, STATUS.PENDING, STATUS.LOADED])
    # DEPRECATED: Only here for backwards compatibility
    def disable(self):
        self.fsm.transition_to(Actor.STATUS.PENDING)

    @verify_status([
        STATUS.LOADED, STATUS.READY, STATUS.PENDING, STATUS.ENABLED,
        STATUS.MIGRATABLE
    ])
    def state(self, remap=None):
        state = {}
        # Manual state handling
        # Not available until after __init__ completes
        state['_managed'] = list(self._managed)
        state['inports'] = {
            port: self.inports[port]._state(remap=remap)
            for port in self.inports
        }
        state['outports'] = {
            port: self.outports[port]._state(remap=remap)
            for port in self.outports
        }
        state['_component_members'] = list(self._component_members)

        # Managed state handling
        for key in self._managed:
            obj = self.__dict__[key]
            if _implements_state(obj):
                try:
                    state[key] = obj.state(remap)
                except:
                    state[key] = obj.state()
            else:
                state[key] = obj

        return state

    @verify_status([STATUS.LOADED, STATUS.READY, STATUS.PENDING])
    def _set_state(self, state):
        # Managed state handling

        # Update since if previously a shadow actor the init has been called first
        # which potentially have altered the managed attributes set compared
        # with the recorded state
        self._managed.update(set(state['_managed']))

        for key in state['_managed']:
            if key not in self.__dict__:
                self.__dict__[key] = state.pop(key)
            else:
                obj = self.__dict__[key]
                if _implements_state(obj):
                    obj.set_state(state.pop(key))
                else:
                    self.__dict__[key] = state.pop(key)

        # Manual state handling
        for port in state['inports']:
            # Uses setdefault to support shadow actor
            self.inports.setdefault(port, actorport.InPort(
                port, self))._set_state(state['inports'][port])
        for port in state['outports']:
            # Uses setdefault to support shadow actor
            self.outports.setdefault(port, actorport.OutPort(
                port, self))._set_state(state['outports'][port])
        self._component_members = set(state['_component_members'])

    # TODO verify status should only allow reading connections when and after being fully connected (enabled)
    @verify_status(
        [STATUS.ENABLED, STATUS.READY, STATUS.PENDING, STATUS.MIGRATABLE])
    def connections(self, node_id):
        c = {'actor_id': self._id, 'actor_name': self._name}
        inports = {}
        for port in self.inports.values():
            peers = [(node_id, p[1]) if p[0] == 'local' else p
                     for p in port.get_peers()]
            inports[port.id] = peers
        c['inports'] = inports
        outports = {}
        for port in self.outports.values():
            peers = [(node_id, p[1]) if p[0] == 'local' else p
                     for p in port.get_peers()]
            outports[port.id] = peers
        c['outports'] = outports
        return c

    def serialize(self):
        return self.state()

    def deserialize(self, data):
        self._set_state(data)

    def exception_handler(self, action, args):
        """Defult handler when encountering ExceptionTokens"""
        _log.error(
            "ExceptionToken encountered\n  name: %s\n  type: %s\n  action: %s\n  args: %s\n"
            % (self._name, self._type, action.__name__, args))
        raise Exception("ExceptionToken NOT HANDLED")

    def events(self):
        return []

    def component_add(self, actor_ids):
        if not isinstance(actor_ids, (set, list, tuple)):
            actor_ids = [actor_ids]
        self._component_members.update(actor_ids)

    def component_remove(self, actor_ids):
        if not isinstance(actor_ids, (set, list, tuple)):
            actor_ids = [actor_ids]
        self._component_members -= set(actor_ids)

    def part_of_component(self):
        return len(self._component_members - set([self._id])) > 0

    def component_members(self):
        return self._component_members

    def requirements_add(self, deploy_reqs, extend=False):
        if extend:
            self._deployment_requirements.extend(deploy_reqs)
        else:
            self._deployment_requirements = deploy_reqs

    def requirements_get(self):
        if self._port_property_capabilities is None:
            self._port_property_capabilities = self._derive_port_property_capabilities(
            )
        capability_port = [{
            'op': 'port_property_match',
            'kwargs': {
                'port_property': self._port_property_capabilities
            },
            'type': '+'
        }]
        if hasattr(self, 'requires'):
            capability_require = [{
                'op': 'actor_reqs_match',
                'kwargs': {
                    'requires': self.requires
                },
                'type': '+'
            }]
        else:
            capability_require = []
        if self._replication_data.id is None:
            replica_nodes = []
        else:
            # exclude node with replicas
            replica_nodes = [{
                'op': 'replica_nodes',
                'kwargs': {},
                'type': '-'
            }]
        return self._deployment_requirements + capability_require + capability_port + replica_nodes

    def _derive_port_property_capabilities(self):
        port_property_capabilities = set([])
        for port in self.inports.values():
            port_property_capabilities.update(
                get_port_property_capabilities(port.properties))
        for port in self.outports.values():
            port_property_capabilities.update(
                get_port_property_capabilities(port.properties))
        _log.debug("derive_port_property_capabilities:" +
                   str(port_property_capabilities))
        return get_port_property_runtime(port_property_capabilities)

    def signature_set(self, signature):
        if self._signature is None:
            self._signature = signature

    def check_authorization_decision(self):
        """Check if authorization decision is still valid"""
        if self.authorization_checks:
            if any(
                    isinstance(elem, list)
                    for elem in self.authorization_checks):
                # If list of lists, True must be found in each list.
                for plugin_list in self.authorization_checks:
                    if not check_authorization_plugin_list(plugin_list):
                        return False
                return True
            else:
                return check_authorization_plugin_list(
                    self.authorization_checks)
        return True

    @verify_status([STATUS.DENIED])
    def set_migration_info(self, reply):
        if reply and reply.status == 200 and reply.data["node_id"]:
            self._migration_info = reply.data
            self.fsm.transition_to(Actor.STATUS.MIGRATABLE)
            _log.info("Migrate actor %s to node %s" %
                      (self._name, self._migration_info["node_id"]))
            # Inform the scheduler that the actor is ready to migrate.
            self._calvinsys.scheduler_maintenance_wakeup()
        else:
            _log.info("No possible migration destination found for actor %s" %
                      self._name)
            # Try to enable/migrate actor again after a delay.
            self._calvinsys.scheduler_maintenance_wakeup(delay=True)

    @verify_status([STATUS.MIGRATABLE, STATUS.READY])
    def remove_migration_info(self, status):
        if status.status != 200:
            self._migration_info = None
コード例 #2
0
class CalvinTunnel(object):
    """CalvinTunnel is a tunnel over the runtime to runtime communication with a peer node"""

    STATUS = enum('PENDING', 'WORKING', 'TERMINATED')

    def __init__(self, links, peer_node_id, tunnel_type, policy, id=None):
        """ links: the calvin networks dictionary of links
            peer_node_id: the id of the peer that we use
            tunnel_type: what is the usage of the tunnel
            policy: TODO not used currently
            id: Tunnel objects on both nodes will use the same id number hence only supply if provided from other side
        """
        super(CalvinTunnel, self).__init__()
        # The tunnel only use one link (links[peer_node_id]) at a time but it can switch at any point
        self.links = links
        self.peer_node_id = peer_node_id
        self.tunnel_type = tunnel_type
        self.policy = policy
        # id may change while status is PENDING, but is fixed in WORKING
        self.id = id if id else calvinuuid.uuid("TUNNEL")
        # If id supplied then we must be the second end and hence working
        self.status = CalvinTunnel.STATUS.WORKING if id else CalvinTunnel.STATUS.PENDING
        # Add the tunnel to the current link (will be migrated for us if switch link)
        if self.peer_node_id in self.links:
            self.links[self.peer_node_id].tunnels[self.id] = self
        # The callbacks recv for incoming message, down for tunnel failed or died, up for tunnel working
        self.recv_handler = None
        self.down_handler = None
        self.up_handler = None

    def _late_link(self, peer_node_id):
        """ Sometimes the peer is unknown even when a tunnel object is needed.
             This method is called when we have the peer node id.
        """
        self.peer_node_id = peer_node_id
        if self.peer_node_id in self.links:
            self.links[self.peer_node_id].tunnels[self.id] = self

    def _update_id(self, id):
        """ While status PENDING we might have a simulataneous tunnel setup
            from both sides. The tunnel with highest id is used, this is
            solved by changing the id for the existing tunnel object with
            this method instead of creating a new and destroying the other.
        """
        old_id = self.id
        self.id = id
        self.links[self.peer_node_id].tunnels.pop(old_id)
        self.links[self.peer_node_id].tunnels[self.id] = self

    def _setup_ack(self, reply):
        """ Gets called when the tunnel request is acknowledged by the other side """
        if reply['tunnel_id'] != self.id:
            self._update_id(reply['tunnel_id'])
        if reply['status'] == 'ACK':
            self.status = CalvinTunnel.STATUS.WORKING
            if self.up_handler:
                self.up_handler()
        else:
            self.status = CalvinTunnel.STATUS.TERMINATED
            if self.down_handler:
                self.down_handler()

    def _destroy_ack(self, reply):
        """ Gets called when the tunnel destruction is acknowledged by the other side """
        self.close(local_only=True)
        if reply != 'ACK':
            raise Exception("Got none ack on destruction of tunnel!\n%s" %
                            reply)

    def send(self, payload):
        """ Send a payload over the tunnel 
            payload must be serializable, i.e. only built-in types such as:
            dict, list, tuple, string, numbers, booleans, etc
        """
        msg = {'cmd': 'TUNNEL_DATA', 'value': payload, 'tunnel_id': self.id}
        self.links[self.peer_node_id].send(msg)

    def register_recv(self, handler):
        """ Register the handler of incoming messages on this tunnel """
        self.recv_handler = handler

    def register_tunnel_down(self, handler):
        """ Register the handler of tunnel down"""
        self.down_handler = handler

    def register_tunnel_up(self, handler):
        """ Register the handler of tunnel up"""
        self.up_handler = handler

    def close(self, local_only=False):
        """ Removes the tunnel but does not inform
            other end when local_only.
            
            Currently does not support local_only == False
        """
        self.status = CalvinTunnel.STATUS.TERMINATED
        self.links[self.peer_node_id].tunnels.pop(self.id)
        if not local_only:
            #FIXME use the tunnel_destroy cmd directly instead
            raise NotImplementedError()
コード例 #3
0
# Copyright (c) 2016 Ericsson AB
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from calvin.utilities.utils import enum
PURPOSE = enum('INIT', 'CONNECT', 'DISCONNECT')

class BaseConnection(object):
    """BaseConnection"""

    def __init__(self, node, purpose, port, peer_port_meta, callback, factory, *args, **kwargs):
        super(BaseConnection, self).__init__()
        self.node = node
        self.purpose = purpose
        self.port = port
        self.peer_port_meta = peer_port_meta
        self.callback = callback
        self.factory = factory
        self._parallel_connections = []

    def async_reply(self, status):
コード例 #4
0
ファイル: common.py プロジェクト: lvjh/calvin-base
# Copyright (c) 2016 Ericsson AB
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from calvin.utilities.utils import enum
PURPOSE = enum('INIT', 'CONNECT', 'DISCONNECT')

class BaseConnection(object):
    """BaseConnection"""

    def __init__(self, node, purpose, port, peer_port_meta, callback, *args, **kwargs):
        super(BaseConnection, self).__init__()
        self.node = node
        self.purpose = purpose
        self.port = port
        self.peer_port_meta = peer_port_meta
        self.callback = callback
        self._parallel_connections = []

    def parallel_connections(self, connections):
        self._parallel_connections = connections
コード例 #5
0
class Actor(object):
    """
    Base class for all actors
    Need a name supplied.
    Subclasses need to declare the parameter
    calvinsys if they want access to system
    interface on the node, this parameter
    will be supplied by the node and not by user
    """

    # Class variable controls action priority order
    action_priority = tuple()

    # Internal state (status)
    class FSM(object):
        def __init__(self,
                     states,
                     initial,
                     transitions,
                     hooks=None,
                     allow_invalid_transitions=True,
                     disable_transition_checks=False,
                     disable_state_checks=False):
            self.states = states
            self._state = initial
            self.transitions = transitions
            self.hooks = hooks or {}
            self.allow_invalid_transitions = allow_invalid_transitions
            self.disable_transition_checks = disable_transition_checks
            # disable_state_checks is used in the verify_status decorator
            self.disable_state_checks = disable_state_checks

        def state(self):
            return self._state

        def transition_to(self, new_state):
            if new_state in self.transitions[
                    self._state] or self.disable_transition_checks:
                hook = self.hooks.get((self._state, new_state), None)
                if hook:
                    hook()
                self._state = new_state
            else:
                msg = "Invalid transition %s -> %s" % (
                    self, self.printable(new_state))
                if self.allow_invalid_transitions:
                    _log.warning("ALLOWING " + msg)
                    self._state = new_state
                else:
                    raise Exception(msg)

        def printable(self, state):
            return self.states.reverse_mapping[state]

        def __str__(self):
            return self.printable(self._state)

    STATUS = enum('LOADED', 'READY', 'PENDING', 'ENABLED', 'DENIED',
                  'MIGRATABLE')

    VALID_TRANSITIONS = {
        STATUS.LOADED: [STATUS.READY],
        STATUS.READY: [STATUS.PENDING, STATUS.ENABLED, STATUS.DENIED],
        STATUS.PENDING: [STATUS.READY, STATUS.PENDING, STATUS.ENABLED],
        STATUS.ENABLED: [STATUS.READY, STATUS.PENDING, STATUS.DENIED],
        STATUS.DENIED: [STATUS.ENABLED, STATUS.MIGRATABLE, STATUS.PENDING],
        STATUS.MIGRATABLE: [STATUS.READY, STATUS.DENIED]
    }

    test_args = ()
    test_kwargs = {}

    # What are the arguments, really?
    def __init__(self,
                 actor_type,
                 name='',
                 allow_invalid_transitions=True,
                 disable_transition_checks=False,
                 disable_state_checks=False,
                 actor_id=None,
                 security=None):
        """Should _not_ be overridden in subclasses."""
        super(Actor, self).__init__()
        self._type = actor_type
        self.name = name  # optional: human_readable_name
        self.id = actor_id or calvinuuid.uuid("ACTOR")
        _log.debug("New actor id: %s, supplied actor id %s" %
                   (self.id, actor_id))
        self._deployment_requirements = []
        self._signature = None
        self._component_members = set(
            [self.id])  # We are only part of component if this is extended
        self._managed = set(
            ('id', 'name', '_deployment_requirements', '_signature',
             'subject_attributes', 'migration_info'))
        self._calvinsys = None
        self._using = {}
        self.control = calvincontrol.get_calvincontrol()
        self.metering = metering.get_metering()
        self.migration_info = None
        self._migrating_to = None  # During migration while on the previous node set to the next node id
        self._last_time_warning = 0.0
        self.sec = security
        self.subject_attributes = self.sec.get_subject_attributes(
        ) if self.sec is not None else None
        self.authorization_checks = None

        self.inports = {
            p: actorport.InPort(p, self)
            for p in self.inport_names
        }
        self.outports = {
            p: actorport.OutPort(p, self)
            for p in self.outport_names
        }

        hooks = {
            (Actor.STATUS.PENDING, Actor.STATUS.ENABLED): self.will_start,
            (Actor.STATUS.ENABLED, Actor.STATUS.PENDING): self.will_stop,
        }
        self.fsm = Actor.FSM(
            Actor.STATUS,
            Actor.STATUS.LOADED,
            Actor.VALID_TRANSITIONS,
            hooks,
            allow_invalid_transitions=allow_invalid_transitions,
            disable_transition_checks=disable_transition_checks,
            disable_state_checks=disable_state_checks)
        self.metering.add_actor_info(self)

    def set_authorization_checks(self, authorization_checks):
        self.authorization_checks = authorization_checks

    @verify_status([STATUS.LOADED])
    def setup_complete(self):
        self.fsm.transition_to(Actor.STATUS.READY)

    def init(self):
        raise Exception("Implementing 'init()' is mandatory.")

    def will_start(self):
        """Override in actor subclass if actions need to be taken before starting."""
        pass

    def will_stop(self):
        """Override in actor subclass if actions need to be taken before stopping."""
        pass

    def will_migrate(self):
        """Override in actor subclass if actions need to be taken before migrating."""
        pass

    def did_migrate(self):
        """Override in actor subclass if actions need to be taken after migrating."""
        pass

    def will_end(self):
        """Override in actor subclass if actions need to be taken before destruction."""
        pass

    def __getitem__(self, attr):
        if attr in self._using:
            return self._using[attr]
        raise KeyError(attr)

    def use(self, requirement, shorthand):
        self._using[shorthand] = self._calvinsys.use_requirement(
            self, requirement)

    def __str__(self):
        ip = ""
        for p in self.inports.values():
            ip = ip + str(p)
        op = ""
        for p in self.outports.values():
            op = op + str(p)
        s = "Actor: '%s' class '%s'\nstatus: %s\ninports: %s\noutports:%s" % (
            self.name, self._type, self.fsm, ip, op)
        return s

    @verify_status([STATUS.READY, STATUS.PENDING])
    def did_connect(self, port):
        """Called when a port is connected, checks actor is fully connected."""
        _log.debug("actor.did_connect BEGIN %s %s " % (self.name, self.id))
        # If we happen to be in READY, go to PENDING
        if self.fsm.state() == Actor.STATUS.READY:
            self.fsm.transition_to(Actor.STATUS.PENDING)
        # Three non-patological options:
        # have inports, have outports, or have in- and outports

        if self.inports:
            for p in self.inports.values():
                if not p.is_connected():
                    return

        if self.outports:
            for p in self.outports.values():
                if not p.is_connected():
                    return

        # If we made it here, all ports are connected
        self.fsm.transition_to(Actor.STATUS.ENABLED)
        _log.debug("actor.did_connect ENABLED %s %s " % (self.name, self.id))

        # Actor enabled, inform scheduler
        self._calvinsys.scheduler_wakeup()

    @verify_status(
        [STATUS.ENABLED, STATUS.PENDING, STATUS.DENIED, STATUS.MIGRATABLE])
    def did_disconnect(self, port):
        """Called when a port is disconnected, checks actor is fully disconnected."""
        # If the actor is MIGRATABLE, return since it will be migrated soon.
        if self.fsm.state() == Actor.STATUS.MIGRATABLE:
            return
        # If we happen to be in ENABLED/DENIED, go to PENDING
        if self.fsm.state() != Actor.STATUS.PENDING:
            self.fsm.transition_to(Actor.STATUS.PENDING)

        # Three non-patological options:
        # have inports, have outports, or have in- and outports
        if self.inports:
            for p in self.inports.values():
                if p.is_connected():
                    return

        if self.outports:
            for p in self.outports.values():
                if p.is_connected():
                    return

        # If we made it here, all ports are disconnected
        self.fsm.transition_to(Actor.STATUS.READY)

    @verify_status([STATUS.ENABLED])
    def fire(self):
        start_time = time.time()
        total_result = ActionResult(did_fire=False)
        while True:
            if not self.check_authorization_decision():
                _log.info("Access denied for actor %s(%s)" %
                          (self._type, self.id))
                # The authorization decision is not valid anymore.
                # Change actor status to DENIED.
                self.fsm.transition_to(Actor.STATUS.DENIED)
                # Try to migrate actor.
                self.sec.authorization_runtime_search(
                    self.id,
                    self._signature,
                    callback=CalvinCB(self.set_migration_info))
                return total_result
            # Re-try action in list order after EVERY firing
            for action_method in self.__class__.action_priority:
                action_result = action_method(self)
                total_result.merge(action_result)
                # Action firing should fire the first action that can fire,
                # hence when fired start from the beginning
                if action_result.did_fire:
                    # FIXME: Make this a hook for the runtime to use, don't
                    #        import and use calvin_control or metering in actor
                    self.metering.fired(self.id, action_method.__name__)
                    self.control.log_actor_firing(
                        self.id, action_method.__name__,
                        action_result.tokens_produced,
                        action_result.tokens_consumed,
                        action_result.production)
                    _log.debug("Actor %s(%s) did fire %s -> %s" %
                               (self._type, self.id, action_method.__name__,
                                str(action_result)))
                    break

            if not action_result.did_fire:
                diff = time.time() - start_time
                if diff > 0.2 and start_time - self._last_time_warning > 120.0:
                    # Every other minute warn if an actor runs for longer than 200 ms
                    self._last_time_warning = start_time
                    _log.warning("%s (%s) actor blocked for %f sec" %
                                 (self.name, self._type, diff))
                # We reached the end of the list without ANY firing => return
                return total_result
        # Redundant as of now, kept as reminder for when rewriting exception handling.
        raise Exception('Exit from fire should ALWAYS be from previous line.')

    def enabled(self):
        return self.fsm.state() == Actor.STATUS.ENABLED

    def denied(self):
        return self.fsm.state() == Actor.STATUS.DENIED

    def migratable(self):
        return self.fsm.state() == Actor.STATUS.MIGRATABLE

    @verify_status([STATUS.DENIED])
    def enable_or_migrate(self):
        """Enable actor if access is permitted. Try to migrate if access still denied."""
        if self.check_authorization_decision():
            self.fsm.transition_to(Actor.STATUS.ENABLED)
        else:
            # Try to migrate actor.
            self.sec.authorization_runtime_search(self.id,
                                                  self._signature,
                                                  callback=CalvinCB(
                                                      self.set_migration_info))

    # DEPRECATED: Only here for backwards compatibility
    @verify_status([STATUS.ENABLED])
    def enable(self):
        self.fsm.transition_to(Actor.STATUS.ENABLED)

    @verify_status([STATUS.READY, STATUS.PENDING, STATUS.LOADED])
    # DEPRECATED: Only here for backwards compatibility
    def disable(self):
        self.fsm.transition_to(Actor.STATUS.PENDING)

    @verify_status(
        [STATUS.LOADED, STATUS.READY, STATUS.PENDING, STATUS.MIGRATABLE])
    def state(self):
        state = {}
        # Manual state handling
        # Not available until after __init__ completes
        state['_managed'] = list(self._managed)
        state['inports'] = {
            port: self.inports[port]._state()
            for port in self.inports
        }
        state['outports'] = {
            port: self.outports[port]._state()
            for port in self.outports
        }
        state['_component_members'] = list(self._component_members)

        # Managed state handling
        for key in self._managed:
            obj = self.__dict__[key]
            if _implements_state(obj):
                state[key] = obj.state()
            else:
                state[key] = obj

        return state

    @verify_status([STATUS.LOADED, STATUS.READY, STATUS.PENDING])
    def _set_state(self, state):
        # Managed state handling

        # Update since if previously a shadow actor the init has been called first
        # which potentially have altered the managed attributes set compared
        # with the recorded state
        self._managed.update(set(state['_managed']))

        for key in state['_managed']:
            if key not in self.__dict__:
                self.__dict__[key] = state.pop(key)
            else:
                obj = self.__dict__[key]
                if _implements_state(obj):
                    obj.set_state(state.pop(key))
                else:
                    self.__dict__[key] = state.pop(key)

        # Manual state handling
        for port in state['inports']:
            # Uses setdefault to support shadow actor
            self.inports.setdefault(port, actorport.InPort(
                port, self))._set_state(state['inports'][port])
        for port in state['outports']:
            # Uses setdefault to support shadow actor
            self.outports.setdefault(port, actorport.OutPort(
                port, self))._set_state(state['outports'][port])
        self._component_members = set(state['_component_members'])

    # TODO verify status should only allow reading connections when and after being fully connected (enabled)
    @verify_status(
        [STATUS.ENABLED, STATUS.READY, STATUS.PENDING, STATUS.MIGRATABLE])
    def connections(self, node_id):
        c = {'actor_id': self.id, 'actor_name': self.name}
        inports = {}
        for port in self.inports.values():
            peers = [(node_id, p[1]) if p[0] == 'local' else p
                     for p in port.get_peers()]
            inports[port.id] = peers
        c['inports'] = inports
        outports = {}
        for port in self.outports.values():
            peers = [(node_id, p[1]) if p[0] == 'local' else p
                     for p in port.get_peers()]
            outports[port.id] = peers
        c['outports'] = outports
        return c

    def serialize(self):
        return self.state()

    def deserialize(self, data):
        self._set_state(data)

    def exception_handler(self, action, args, context):
        """Defult handler when encountering ExceptionTokens"""
        _log.error(
            "ExceptionToken encountered\n  name: %s\n  type: %s\n  action: %s\n  args: %s\n  context: %s\n"
            % (self.name, self._type, action.__name__, args, context))
        raise Exception("ExceptionToken NOT HANDLED")

    def events(self):
        return []

    def component_add(self, actor_ids):
        if not isinstance(actor_ids, (set, list, tuple)):
            actor_ids = [actor_ids]
        self._component_members.update(actor_ids)

    def component_remove(self, actor_ids):
        if not isinstance(actor_ids, (set, list, tuple)):
            actor_ids = [actor_ids]
        self._component_members -= set(actor_ids)

    def part_of_component(self):
        return len(self._component_members - set([self.id])) > 0

    def component_members(self):
        return self._component_members

    def requirements_add(self, deploy_reqs, extend=False):
        if extend:
            self._deployment_requirements.extend(deploy_reqs)
        else:
            self._deployment_requirements = deploy_reqs

    def requirements_get(self):
        return self._deployment_requirements + ([{
            'op': 'actor_reqs_match',
            'kwargs': {
                'requires': self.requires
            },
            'type': '+'
        }] if hasattr(self, 'requires') else [])

    def signature_set(self, signature):
        if self._signature is None:
            self._signature = signature

    def check_authorization_decision(self):
        """Check if authorization decision is still valid"""
        if self.authorization_checks:
            if any(
                    isinstance(elem, list)
                    for elem in self.authorization_checks):
                # If list of lists, True must be found in each list.
                for plugin_list in self.authorization_checks:
                    if not check_authorization_plugin_list(plugin_list):
                        return False
                return True
            else:
                return check_authorization_plugin_list(
                    self.authorization_checks)
        return True

    @verify_status([STATUS.DENIED])
    def set_migration_info(self, reply):
        if reply and reply.status == 200 and reply.data["node_id"]:
            self.migration_info = reply.data
            self.fsm.transition_to(Actor.STATUS.MIGRATABLE)
            _log.info("Migrate actor %s to node %s" %
                      (self.name, self.migration_info["node_id"]))
            # Inform the scheduler that the actor is ready to migrate.
            self._calvinsys.scheduler_maintenance_wakeup()
        else:
            _log.info("No possible migration destination found for actor %s" %
                      self.name)
            # Try to enable/migrate actor again after a delay.
            self._calvinsys.scheduler_maintenance_wakeup(delay=True)

    @verify_status([STATUS.MIGRATABLE, STATUS.READY])
    def remove_migration_info(self, status):
        if status.status != 200:
            self.migration_info = None
コード例 #6
0
from calvin.utilities import certificate_authority
from calvin.utilities import calvinconfig
from calvin.utilities import calvinlogger

_log = calvinlogger.get_logger(__name__)

STATE = enum(
    "NEW",  # Object is initiated but not loaded or started.
    "CONFIGURED",  # An acceptable configuration is loaded.
    "LISTENING",  # Listen for request or response.
    "TRANSMIT",  # Transmit a request or response.
    "SATISFIED",  # Required security discoveries established.
    "CSR_GENERATED",  # CSR has been generated.
    "CSR_REQUESTED",  # CSR has been requested.
    "CERTIFICATE_RECEIVED",  # Certificate has been received.
    "CERTIFICATE_VALID",  # Certificate validly signed by CA.
    "CA_ACCEPTED_CONFIGURATION",  # CAcert meets config requirements.
    "CERTIFICATE_ACCEPTED_CONFIGURATION",  # Certificate meets req.
    "CERTIFICATE_STORED",  # Certificate stored.
    "CA_GENERATED",  # CA cert generated.
    "CSR_RECEIVED",  # CSR has been received.
    "CSR_ACCEPTED",  # CSR meets config requirements.
    "CSR_STORED",  # CSR stored in new csr location
    "CSR_SIGNED",  # CSR have been signed by CA.
    "CSR_ALREDY_SIGNED",  # The requested CSR already signed.
    "CERTIFICATE_GENERATED")  # Certificate generated by CA


# Exceptions
class ConfigurationMalformed(Exception):
    """Configuration is missing required attributes to set policy."""
    pass
コード例 #7
0
ファイル: actor.py プロジェクト: amar123deep/calvin-base
class Actor(object):
    """
    Base class for all actors
    Need a name supplied.
    Subclasses need to declare the parameter
    calvinsys if they want access to system
    interface on the node, this parameter
    will be supplied by the node and not by user
    """

    # Class variable controls action priority order
    action_priority = tuple()

    # Internal state (status)
    class FSM(object):
        def __init__(self,
                     states,
                     initial,
                     transitions,
                     hooks=None,
                     allow_invalid_transitions=True,
                     disable_transition_checks=False,
                     disable_state_checks=False):
            self.states = states
            self._state = initial
            self.transitions = transitions
            self.hooks = hooks or {}
            self.allow_invalid_transitions = allow_invalid_transitions
            self.disable_transition_checks = disable_transition_checks
            # disable_state_checks is used in the verify_status decorator
            self.disable_state_checks = disable_state_checks

        def state(self):
            return self._state

        def transition_to(self, new_state):
            if new_state in self.transitions[
                    self._state] or self.disable_transition_checks:
                hook = self.hooks.get((self._state, new_state), None)
                if hook:
                    hook()
                self._state = new_state
            else:
                msg = "Invalid transition %s -> %s" % (
                    self, self.printable(new_state))
                if self.allow_invalid_transitions:
                    _log.warning("ALLOWING " + msg)
                    self._state = new_state
                else:
                    raise Exception(msg)

        def printable(self, state):
            return self.states.reverse_mapping[state]

        def __str__(self):
            return self.printable(self._state)

    STATUS = enum('LOADED', 'READY', 'PENDING', 'ENABLED')

    VALID_TRANSITIONS = {
        STATUS.LOADED: [STATUS.READY],
        STATUS.READY: [STATUS.PENDING, STATUS.ENABLED],
        STATUS.PENDING: [STATUS.READY, STATUS.PENDING, STATUS.ENABLED],
        STATUS.ENABLED: [STATUS.READY, STATUS.PENDING]
    }

    test_args = ()
    test_kwargs = {}

    # What are the arguments, really?
    def __init__(self,
                 actor_type,
                 name='',
                 allow_invalid_transitions=True,
                 disable_transition_checks=False,
                 disable_state_checks=False):
        """Should normally _not_ be overridden in subclasses."""
        super(Actor, self).__init__()
        self._type = actor_type
        self.name = name  # optional: human_readable_name
        self.id = calvinuuid.uuid("ACTOR")
        self._managed = set(('id', 'name'))
        self.calvinsys = None  # CalvinSys(node)
        self.control = calvincontrol.get_calvincontrol()

        self.inports = {
            p: actorport.InPort(p, self)
            for p in self.inport_names
        }
        self.outports = {
            p: actorport.OutPort(p, self)
            for p in self.outport_names
        }

        hooks = {
            (Actor.STATUS.PENDING, Actor.STATUS.ENABLED): self.will_start,
            (Actor.STATUS.ENABLED, Actor.STATUS.PENDING): self.will_stop,
        }
        self.fsm = Actor.FSM(
            Actor.STATUS,
            Actor.STATUS.LOADED,
            Actor.VALID_TRANSITIONS,
            hooks,
            allow_invalid_transitions=allow_invalid_transitions,
            disable_transition_checks=disable_transition_checks,
            disable_state_checks=disable_state_checks)

    @verify_status([STATUS.LOADED])
    def setup_complete(self):
        self.fsm.transition_to(Actor.STATUS.READY)

    def init(self):
        raise Exception("Implementing 'init()' is mandatory.")

    def will_start(self):
        """Override in actor subclass if actions need to be taken before starting."""
        pass

    def will_stop(self):
        """Override in actor subclass if actions need to be taken before stopping."""
        pass

    def will_migrate(self):
        """Override in actor subclass if actions need to be taken before migrating."""
        pass

    def did_migrate(self):
        """Override in actor subclass if actions need to be taken after migrating."""
        pass

    def will_end(self):
        """Override in actor subclass if actions need to be taken before destruction."""
        pass

    @verify_status([STATUS.LOADED])
    def attach_API(self, api_name, api):
        # FIXME: This should really go into a dict where each required API is granted
        #        by the actor manager (if security allows), e.g.
        #        self.calvinsys = {'event':evt, 'timer':tmr, 'socket':sck}
        self.calvinsys = api

    def __str__(self):
        ip = ""
        for p in self.inports.values():
            ip = ip + str(p)
        op = ""
        for p in self.outports.values():
            op = op + str(p)
        s = "Actor: '%s' class '%s'\nstatus: %s\ninports: %s\noutports:%s" % (
            self.name, self._type, self.fsm, ip, op)
        return s

    @verify_status([STATUS.READY])
    def set_port_property(self, port_type, port_name, port_property, value):
        """Change a port property. Currently, setting 'fanout' on output ports is only allowed operation."""

        if port_type not in ('in', 'out'):
            _log.error("Illegal port type '%s' for actor '%s' of type '%s'" %
                       (port_type, self.name, self._type))
            return False
        ports = self.outports if port_type == 'out' else self.inports
        if port_name not in ports:
            _log.error("Illegal %sport name '%s' for actor '%s' of type '%s'" %
                       (port_type, port_name, self.name, self._type))
            return False
        port = ports[port_name]
        if not hasattr(port, port_property):
            _log.error(
                "Illegal property '%s' for %sport '%s' in actor '%s' of type '%s'"
                % (port_property, port_type, port_name, self.name, self._type))
            return False
        setattr(port, port_property, value)
        return True

    @verify_status([STATUS.READY, STATUS.PENDING])
    def did_connect(self, port):
        """Called when a port is connected, checks actor is fully connected."""
        # If we happen to by in READY, go to PENDING
        if self.fsm.state() == Actor.STATUS.READY:
            self.fsm.transition_to(Actor.STATUS.PENDING)
        # Three non-patological options:
        # have inports, have outports, or have in- and outports
        if self.inports:
            for p in self.inports.values():
                if not p.is_connected():
                    return
        if self.outports:
            for p in self.outports.values():
                if not p.is_connected():
                    return
        # If we made it here, all ports are connected
        self.fsm.transition_to(Actor.STATUS.ENABLED)
        self.calvinsys.events.timer._trigger_loop()

    @verify_status([STATUS.ENABLED, STATUS.PENDING])
    def did_disconnect(self, port):
        """Called when a port is disconnected, checks actor is fully disconnected."""
        # If we happen to by in ENABLED, go to PENDING
        if self.fsm.state() == Actor.STATUS.ENABLED:
            self.fsm.transition_to(Actor.STATUS.PENDING)
        # Three non-patological options:
        # have inports, have outports, or have in- and outports
        if self.inports:
            for p in self.inports.values():
                if p.is_connected():
                    return
        if self.outports:
            for p in self.outports.values():
                if p.is_connected():
                    return
        # If we made it here, all ports are disconnected
        self.fsm.transition_to(Actor.STATUS.READY)

    @verify_status([STATUS.ENABLED])
    def fire(self):
        total_result = ActionResult(did_fire=False)
        while True:
            # Re-try action in list order after EVERY firing
            for action_method in self.__class__.action_priority:
                action_result = action_method(self)
                total_result.merge(action_result)
                # Action firing should fire the first action that can fire,
                # hence when fired start from the beginning
                if action_result.did_fire:
                    self.control.log_firing(self.name, action_method.__name__,
                                            action_result.tokens_produced,
                                            action_result.tokens_consumed,
                                            action_result.production)
                    break
            if not action_result.did_fire:
                # We reached the end of the list without ANY firing => return
                return total_result
        # Redundant as of now, kept as reminder for when rewriting exeption handling.
        raise Exception('Exit from fire should ALWAYS be from previous line.')

    def enabled(self):
        self.calvinsys.events.timer._trigger_loop()
        return self.fsm.state() == Actor.STATUS.ENABLED

    # DEPRECATED: Only here for backwards compatibility
    @verify_status([STATUS.ENABLED])
    def enable(self):
        self.fsm.transition_to(Actor.STATUS.ENABLED)

    @verify_status([STATUS.READY, STATUS.PENDING, STATUS.LOADED])
    # DEPRECATED: Only here for backwards compatibility
    def disable(self):
        self.fsm.transition_to(Actor.STATUS.PENDING)

    @verify_status([STATUS.LOADED, STATUS.READY, STATUS.PENDING])
    def state(self):
        state = {}
        # Manual state handling
        # Not available until after __init__ completes
        state['_managed'] = list(self._managed)
        state['inports'] = {
            port: self.inports[port]._state()
            for port in self.inports
        }
        state['outports'] = {
            port: self.outports[port]._state()
            for port in self.outports
        }

        # Managed state handling
        for key in self._managed:
            obj = self.__dict__[key]
            if _implements_state(obj):
                state[key] = obj.state()
            else:
                state[key] = obj

        return state

    @verify_status([STATUS.LOADED, STATUS.READY, STATUS.PENDING])
    def set_state(self, state):
        # Managed state handling
        self._managed = set(state['_managed'])
        for key in self._managed:
            if key not in self.__dict__:
                self.__dict__[key] = state.pop(key)
            else:
                obj = self.__dict__[key]
                if _implements_state(obj):
                    obj.set_state(state.pop(key))
                else:
                    self.__dict__[key] = state.pop(key)

        # Manual state handling
        for port in state['inports']:
            self.inports[port]._set_state(state['inports'][port])
        for port in state['outports']:
            self.outports[port]._set_state(state['outports'][port])

    @verify_status([STATUS.ENABLED])
    def connections(self, node_id):
        c = {'actor_id': self.id, 'actor_name': self.name}
        inports = {}
        for port in self.inports.values():
            peer = port.get_peer()
            if peer[0] == 'local':
                peer = (node_id, peer[1])
            inports[port.id] = peer
        c['inports'] = inports
        outports = {}
        for port in self.outports.values():
            peers = [(node_id, p[1]) if p[0] == 'local' else p
                     for p in port.get_peers()]
            outports[port.id] = peers
        c['outports'] = outports
        return c

    def serialize(self):
        return self.state()

    def deserialize(self, data):
        self.set_state(data)

    def exception_handler(self, action, args, context):
        """Defult handler when encountering ExceptionTokens"""
        _log.error(
            "ExceptionToken encountered\n  name: %s\n  type: %s\n  action: %s\n  args: %s\n  context: %s\n"
            % (self.name, self._type, action.__name__, args, context))
        raise Exception("ExceptionToken NOT HANDLED")

    def events(self):
        return []
コード例 #8
0
# -*- coding: utf-8 -*-

# Copyright (c) 2017 Ericsson AB
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from calvin.utilities.utils import enum

REPLICATION_STATUS = enum('UNUSED', 'READY', 'REPLICATING', 'DEREPLICATING',
                          'INHIBATED')
PRE_CHECK = enum('NO_OPERATION', 'SCALE_OUT', 'SCALE_IN')
コード例 #9
0
ファイル: actor.py プロジェクト: shashi12533/calvin-base
class Actor(object):
    """
    Base class for all actors
    Need a name supplied.
    Subclasses need to declare the parameter
    calvinsys if they want access to system
    interface on the node, this parameter
    will be supplied by the node and not by user
    """

    # Class variable controls action priority order
    action_priority = tuple()

    # These are the security variables that will always be serialized, see serialize()/deserialize() below
    _security_state_keys = ('_subject_attributes')

    # These are the instance variables that will always be serialized, see serialize()/deserialize() below
    _private_state_keys = ('_id', '_name', '_has_started',
                           '_deployment_requirements', '_signature',
                           '_migration_info', "_port_property_capabilities",
                           "_replication_id")

    # Internal state (status)
    class FSM(object):
        def __init__(self,
                     states,
                     initial,
                     transitions,
                     hooks=None,
                     allow_invalid_transitions=True,
                     disable_transition_checks=False,
                     disable_state_checks=False):
            self.states = states
            self._state = initial
            self.transitions = transitions
            self.hooks = hooks or {}
            self.allow_invalid_transitions = allow_invalid_transitions
            self.disable_transition_checks = disable_transition_checks
            # disable_state_checks is used in the verify_status decorator
            self.disable_state_checks = disable_state_checks

        def state(self):
            return self._state

        def transition_to(self, new_state):
            if new_state in self.transitions[
                    self._state] or self.disable_transition_checks:
                hook = self.hooks.get((self._state, new_state), None)
                if hook:
                    hook()
                self._state = new_state
            else:
                msg = "Invalid transition %s -> %s" % (
                    self, self.printable(new_state))
                if self.allow_invalid_transitions:
                    _log.warning("ALLOWING " + msg)
                    self._state = new_state
                else:
                    raise Exception(msg)

        def printable(self, state):
            return self.states.reverse_mapping[state]

        def __str__(self):
            return self.printable(self._state)

    STATUS = enum('LOADED', 'READY', 'PENDING', 'ENABLED', 'DENIED',
                  'MIGRATABLE')

    VALID_TRANSITIONS = {
        STATUS.LOADED: [STATUS.READY],
        STATUS.READY: [STATUS.PENDING, STATUS.ENABLED, STATUS.DENIED],
        STATUS.PENDING: [STATUS.READY, STATUS.PENDING, STATUS.ENABLED],
        STATUS.ENABLED: [STATUS.READY, STATUS.PENDING, STATUS.DENIED],
        STATUS.DENIED: [STATUS.ENABLED, STATUS.MIGRATABLE, STATUS.PENDING],
        STATUS.MIGRATABLE: [STATUS.READY, STATUS.DENIED]
    }

    test_args = ()
    test_kwargs = {}

    @property
    def id(self):
        return self._id

    @property
    def name(self):
        return self._name

    @name.setter
    def name(self, value):
        self._name = value

    @property
    def migration_info(self):
        return self._migration_info

    # What are the arguments, really?
    def __init__(self,
                 actor_type,
                 name='',
                 allow_invalid_transitions=True,
                 disable_transition_checks=False,
                 disable_state_checks=False,
                 actor_id=None,
                 security=None):
        """Should _not_ be overridden in subclasses."""
        super(Actor, self).__init__()
        self._type = actor_type
        self._name = name  # optional: human_readable_name
        self._id = actor_id or calvinuuid.uuid("ACTOR")
        _log.debug("New actor id: %s, supplied actor id %s" %
                   (self._id, actor_id))
        self._deployment_requirements = []
        self._port_property_capabilities = None
        self._signature = None
        self._component_members = set(
            [self._id])  # We are only part of component if this is extended
        self._managed = set()
        self._has_started = False
        # self.control = calvincontrol.get_calvincontrol()
        self._migration_info = None
        self._migrating_to = None  # During migration while on the previous node set to the next node id
        self._migration_connected = True  # False while setup the migrated actor, to prevent further migrations
        self._last_time_warning = 0.0
        self.sec = security
        self._subject_attributes = self.sec.get_subject_attributes(
        ) if self.sec is not None else None
        self.authorization_checks = None
        self._replication_id = ReplicationId()
        self._exhaust_cb = None
        self._pressure_event = 0  # Time of last pressure event time (not in state only local)

        self.inports = {
            p: actorport.InPort(p, self, pp)
            for p, pp in self.inport_properties.items()
        }
        self.outports = {
            p: actorport.OutPort(p, self, pp)
            for p, pp in self.outport_properties.items()
        }

        hooks = {
            (Actor.STATUS.PENDING, Actor.STATUS.ENABLED): self._will_start,
            (Actor.STATUS.ENABLED, Actor.STATUS.PENDING): self.will_stop,
        }
        self.fsm = Actor.FSM(
            Actor.STATUS,
            Actor.STATUS.LOADED,
            Actor.VALID_TRANSITIONS,
            hooks,
            allow_invalid_transitions=allow_invalid_transitions,
            disable_transition_checks=disable_transition_checks,
            disable_state_checks=disable_state_checks)

    def set_authorization_checks(self, authorization_checks):
        self.authorization_checks = authorization_checks

    @verify_status([STATUS.LOADED])
    def setup_complete(self):
        self.fsm.transition_to(Actor.STATUS.READY)

    def init(self):
        raise Exception("Implementing 'init()' is mandatory.")

    def _will_start(self):
        """Ensure will_start() is only called once"""
        if not self._has_started:
            self.will_start()
            self._has_started = True

    def will_start(self):
        """Override in actor subclass if actions need to be taken before starting."""
        pass

    def will_stop(self):
        """Override in actor subclass if actions need to be taken before stopping."""
        pass

    def will_migrate(self):
        """Override in actor subclass if actions need to be taken before migrating."""
        pass

    def did_migrate(self):
        """Override in actor subclass if actions need to be taken after migrating."""
        pass

    def _will_end(self):
        if hasattr(self, "will_end") and callable(self.will_end):
            self.will_end()
        get_calvinsys().close_all(self)

    def did_replicate(self, index):
        """Override in actor subclass if actions need to be taken after replication."""
        pass

    def __str__(self):
        ip = ""
        for p in self.inports.values():
            ip = ip + str(p)
        op = ""
        for p in self.outports.values():
            op = op + str(p)
        s = "Actor: '%s' class '%s'\nstatus: %s\ninports: %s\noutports:%s" % (
            self._name, self._type, self.fsm, ip, op)
        return s

    @verify_status([STATUS.READY, STATUS.PENDING, STATUS.ENABLED])
    def did_connect(self, port):
        """Called when a port is connected, checks actor is fully connected."""
        if self.fsm.state() == Actor.STATUS.ENABLED:
            # We already was enabled thats fine now with dynamic port connections
            return
        _log.debug("actor.did_connect BEGIN %s %s " % (self._name, self._id))
        # If we happen to be in READY, go to PENDING
        if self.fsm.state() == Actor.STATUS.READY:
            self.fsm.transition_to(Actor.STATUS.PENDING)
        # Three non-patological options:
        # have inports, have outports, or have in- and outports

        if self.inports:
            for p in self.inports.values():
                if not p.is_connected():
                    return

        if self.outports:
            for p in self.outports.values():
                if not p.is_connected():
                    return

        # If we made it here, all ports are connected
        self.fsm.transition_to(Actor.STATUS.ENABLED)
        _log.debug("actor.did_connect ENABLED %s %s " % (self._name, self._id))

    @verify_status(
        [STATUS.ENABLED, STATUS.PENDING, STATUS.DENIED, STATUS.MIGRATABLE])
    def did_disconnect(self, port):
        """Called when a port is disconnected, checks actor is fully disconnected."""
        # If the actor is MIGRATABLE, return since it will be migrated soon.
        _log.debug("Actor %s did_disconnect %s" %
                   (self._id, Actor.STATUS.reverse_mapping[self.fsm.state()]))
        if self.fsm.state() == Actor.STATUS.MIGRATABLE:
            return
        # If we happen to be in ENABLED/DENIED, go to PENDING
        if self.fsm.state() != Actor.STATUS.PENDING:
            self.fsm.transition_to(Actor.STATUS.PENDING)

        # Three non-patological options:
        # have inports, have outports, or have in- and outports
        if self.inports:
            for p in self.inports.values():
                if p.is_connected():
                    return

        if self.outports:
            for p in self.outports.values():
                if p.is_connected():
                    return

        # If we made it here, all ports are disconnected
        self.fsm.transition_to(Actor.STATUS.READY)

    def exhaust(self, callback):
        self._exhaust_cb = callback

    def get_pressure(self):
        _log.debug("get_pressure %s" % self._replication_id.measure_pressure())
        if not self._replication_id.measure_pressure():
            return None
        t = time.time()
        pressure = {}
        for port in self.inports.values():
            for e in port.endpoints:
                PRESSURE_LENGTH = len(e.pressure)
                pressure[port.id + "," + e.peer_id] = {
                    'last':
                    e.pressure_last,
                    'count':
                    e.pressure_count,
                    'pressure': [
                        e.pressure[i % PRESSURE_LENGTH] for i in range(
                            max(0, e.pressure_count -
                                PRESSURE_LENGTH), e.pressure_count)
                    ]
                }
        pressure_event = False
        for p in pressure.values():
            if len(p['pressure']) < 2:
                continue
            if ((p['pressure'][-1][1] - p['pressure'][-2][1]) < 10
                    and p['pressure'][-1][1] > self._pressure_event):
                # Less than 10 sec between queue full and not reported, maybe scale out
                self._pressure_event = max(p['pressure'][-1][1],
                                           self._pressure_event)
                pressure_event = True
                break
            if (p['pressure'][-1][1] < (t - 30)
                    and p['last'] > p['pressure'][-1][0] + 3
                    and p['pressure'][-1][1] > self._pressure_event):
                # More than 30 sec since queue full, received at least 3 tokens and not reported, maybe scale in
                self._pressure_event = max(p['pressure'][-1][1],
                                           self._pressure_event)
                pressure_event = True
                break
        pressure['time'] = t
        _log.debug("get_pressure pressure_event:%s, pressure: %s" %
                   (pressure_event, pressure))
        return pressure if pressure_event else None

    #
    # FIXME: The following methods (_authorized, _warn_slow_actor, _handle_exhaustion) were
    #        extracted from fire() to make the logic easier to follow
    # FIXME: Responsibility of scheduler, not actor class
    #
    def _authorized(self):
        authorized = self.check_authorization_decision()
        if not authorized:
            _log.info("Access denied for actor %s(%s)" %
                      (self._type, self._id))
            # The authorization decision is not valid anymore.
            # Change actor status to DENIED.
            self.fsm.transition_to(Actor.STATUS.DENIED)
            # Try to migrate actor.
            self.sec.authorization_runtime_search(self._id,
                                                  self._signature,
                                                  callback=CalvinCB(
                                                      self.set_migration_info))
        return authorized

    def _warn_slow_actor(self, time_spent, start_time):
        time_since_warning = start_time - self._last_time_warning
        if time_since_warning < 120.0:
            return
        self._last_time_warning = start_time
        _log.warning("%s (%s) actor blocked for %f sec" %
                     (self._name, self._type, time_spent))

    def _handle_exhaustion(self, exhausted_ports, output_ok):
        _log.debug("actor_fire %s test exhaust %s, %s, %s" %
                   (self._id, self._exhaust_cb
                    is not None, exhausted_ports, output_ok))
        for port in exhausted_ports:
            # Might result in actor changing to PENDING
            try:
                port.finished_exhaustion()
            except:
                _log.exception("FINSIHED EXHAUSTION FAILED")
        if (output_ok and self._exhaust_cb is not None and not any([
                p.any_outstanding_exhaustion_tokens()
                for p in self.inports.values()
        ])):
            _log.debug("actor %s exhausted" % self._id)
            # We are in exhaustion, got all exhaustion tokens from peer ports
            # but stopped firing while outport token slots available, i.e. exhausted inports or deadlock
            # FIXME handle exhaustion deadlock
            # Initiate disconnect of outports and destroy the actor
            async .DelayedCall(0,
                               self._exhaust_cb,
                               status=response.CalvinResponse(True))
            self._exhaust_cb = None

    @verify_status([STATUS.ENABLED])
    def fire(self):
        """
        Fire an actor.
        Returns tuple (did_fire, output_ok, exhausted)
        """
        #
        # Go over the action priority list once
        #
        for action_method in self.__class__.action_priority:
            did_fire, output_ok, exhausted = action_method(self)
            # Action firing should fire the first action that can fire
            if did_fire:
                break
        return did_fire, output_ok, exhausted

    def enabled(self):
        # We want to run even if not fully connected during exhaustion
        r = self.fsm.state(
        ) == Actor.STATUS.ENABLED or self._exhaust_cb is not None
        if not r:
            _log.debug("Actor %s %s not enabled" % (self._name, self._id))
        return r

    def denied(self):
        return self.fsm.state() == Actor.STATUS.DENIED

    def migratable(self):
        return self.fsm.state() == Actor.STATUS.MIGRATABLE

    @verify_status([STATUS.DENIED])
    def enable_or_migrate(self):
        """Enable actor if access is permitted. Try to migrate if access still denied."""
        if self.check_authorization_decision():
            self.fsm.transition_to(Actor.STATUS.ENABLED)
        else:
            # Try to migrate actor.
            self.sec.authorization_runtime_search(self._id,
                                                  self._signature,
                                                  callback=CalvinCB(
                                                      self.set_migration_info))

    # DEPRECATED: Only here for backwards compatibility
    @verify_status([STATUS.ENABLED])
    def enable(self):
        self.fsm.transition_to(Actor.STATUS.ENABLED)

    @verify_status([STATUS.READY, STATUS.PENDING, STATUS.LOADED])
    # DEPRECATED: Only here for backwards compatibility
    def disable(self):
        self.fsm.transition_to(Actor.STATUS.PENDING)

    # TODO verify status should only allow reading connections when and after being fully connected (enabled)
    @verify_status(
        [STATUS.ENABLED, STATUS.READY, STATUS.PENDING, STATUS.MIGRATABLE])
    def connections(self, node_id):
        c = {'actor_id': self._id, 'actor_name': self._name}
        inports = {}
        for port in self.inports.values():
            peers = [(node_id, p[1]) if p[0] == 'local' else p
                     for p in port.get_peers()]
            inports[port.id] = peers
        c['inports'] = inports
        outports = {}
        for port in self.outports.values():
            peers = [(node_id, p[1]) if p[0] == 'local' else p
                     for p in port.get_peers()]
            outports[port.id] = peers
        c['outports'] = outports
        return c

    def state(self):
        """Serialize custom state, implement in subclass if necessary"""
        return {}

    def set_state(self, state):
        """Deserialize and set custom state, implement in subclass if necessary"""
        pass

    def _private_state(self):
        """Serialize state common to all actors"""
        state = {}
        state['inports'] = {
            port: self.inports[port]._state()
            for port in self.inports
        }
        state['outports'] = {
            port: self.outports[port]._state()
            for port in self.outports
        }
        state['_component_members'] = list(self._component_members)
        # Place requires in state, in the event we become a ShadowActor
        state['_requires'] = self.requires if hasattr(self, 'requires') else []

        # FIXME: The objects in _private_state_keys are well known, they are private after all,
        #        and we shouldn't need this generic handler.
        for key in self._private_state_keys:
            obj = self.__dict__[key]
            if _implements_state(obj):
                state[key] = obj.state()
            else:
                state[key] = obj

        state["_calvinsys"] = get_calvinsys().serialize(actor=self)

        return state

    def _set_private_state(self, state):
        """Deserialize and apply state common to all actors"""
        if "_calvinsys" in state:
            get_calvinsys().deserialize(actor=self,
                                        csobjects=state["_calvinsys"])
        for port in state['inports']:
            # Uses setdefault to support shadow actor
            self.inports.setdefault(port, actorport.InPort(
                port, self))._set_state(state['inports'][port])
        for port in state['outports']:
            # Uses setdefault to support shadow actor
            self.outports.setdefault(port, actorport.OutPort(
                port, self))._set_state(state['outports'][port])
        self._component_members = set(state['_component_members'])

        # FIXME: The objects in _private_state_keys are well known, they are private after all,
        #        and we shouldn't need this generic handler.
        for key in self._private_state_keys:
            if key not in self.__dict__:
                self.__dict__[key] = state.get(key, None)
            else:
                obj = self.__dict__[key]
                if _implements_state(obj):
                    obj.set_state(state.get(key))
                else:
                    self.__dict__[key] = state.get(key, None)

    def _replication_state(self):
        return None

    def _set_replication_state(self, state):
        """Deserialize and apply state related to a replicating actor """
        pass

    def _security_state(self):
        """
        Serialize security state.
        Security state can only contain objects that can be JSON-serialized.
        """
        return {'_subject_attributes': self._subject_attributes}

    def _set_security_state(self, state):
        """
        Deserialize and apply security state.
        Security state can only contain objects that can be JSON-serialized.
        """
        pass

    def _managed_state(self):
        """
        Serialize managed state.
        Managed state can only contain objects that can be JSON-serialized.
        """
        state = {key: self.__dict__[key] for key in self._managed}
        return state

    def _set_managed_state(self, state):
        """
        Deserialize and apply managed state.
        Managed state can only contain objects that can be JSON-serialized.
        """
        self._managed.update(set(state.keys()))
        for key, val in state.iteritems():
            self.__dict__[key] = val

    def serialize(self):
        """Returns the serialized state of an actor."""
        state = {}
        state['private'] = self._private_state()
        rstate = self._replication_state()
        if rstate is not None:
            state['replication'] = rstate
        state['managed'] = self._managed_state()
        state['security'] = self._security_state()
        state['custom'] = self.state()
        return state

    def deserialize(self, state):
        """Restore an actor's state from the serialized state."""
        self._set_private_state(state['private'])
        self._set_replication_state(state.get('replication', None))
        self._set_security_state(state['security'])
        self._set_managed_state(state['managed'])
        self.set_state(state['custom'])

    def exception_handler(self, action, args):
        """Defult handler when encountering ExceptionTokens"""
        _log.error(
            "ExceptionToken encountered\n  name: %s\n  type: %s\n  action: %s\n  args: %s\n"
            % (self._name, self._type, action.__name__, args))
        raise Exception("ExceptionToken NOT HANDLED")

    def events(self):
        return []

    def component_add(self, actor_ids):
        if not isinstance(actor_ids, (set, list, tuple)):
            actor_ids = [actor_ids]
        self._component_members.update(actor_ids)

    def component_remove(self, actor_ids):
        if not isinstance(actor_ids, (set, list, tuple)):
            actor_ids = [actor_ids]
        self._component_members -= set(actor_ids)

    def part_of_component(self):
        return len(self._component_members - set([self._id])) > 0

    def component_members(self):
        return self._component_members

    def requirements_add(self, deploy_reqs, extend=False):
        if extend:
            self._deployment_requirements.extend(deploy_reqs)
        else:
            self._deployment_requirements = deploy_reqs

    def requirements_get(self):
        if self._port_property_capabilities is None:
            self._port_property_capabilities = self._derive_port_property_capabilities(
            )
        capability_port = [{
            'op': 'port_property_match',
            'kwargs': {
                'port_property': self._port_property_capabilities
            },
            'type': '+'
        }]
        if hasattr(self, 'requires') and self.requires:
            capability_require = [{
                'op': 'actor_reqs_match',
                'kwargs': {
                    'requires': self.requires
                },
                'type': '+'
            }]
        else:
            capability_require = []

        return (self._deployment_requirements + capability_require +
                capability_port + self._replication_id._placement_req)

    def _derive_port_property_capabilities(self):
        port_property_capabilities = set([])
        for port in self.inports.values():
            port_property_capabilities.update(
                get_port_property_capabilities(port.properties))
        for port in self.outports.values():
            port_property_capabilities.update(
                get_port_property_capabilities(port.properties))
        _log.debug("derive_port_property_capabilities:" +
                   str(port_property_capabilities))
        return get_port_property_runtime(port_property_capabilities)

    def signature_set(self, signature):
        if self._signature is None:
            self._signature = signature

    def check_authorization_decision(self):
        """Check if authorization decision is still valid"""
        if self.authorization_checks:
            if any(
                    isinstance(elem, list)
                    for elem in self.authorization_checks):
                # If list of lists, True must be found in each list.
                for plugin_list in self.authorization_checks:
                    if not check_authorization_plugin_list(plugin_list):
                        return False
                return True
            else:
                return check_authorization_plugin_list(
                    self.authorization_checks)
        return True

    @verify_status([STATUS.DENIED])
    def set_migration_info(self, reply):
        if reply and reply.status == 200 and reply.data["node_id"]:
            self._migration_info = reply.data
            self.fsm.transition_to(Actor.STATUS.MIGRATABLE)
            _log.info("Migrate actor %s to node %s" %
                      (self._name, self._migration_info["node_id"]))
            # Inform the scheduler that the actor is ready to migrate.
            get_calvinsys().scheduler_maintenance_wakeup()
        else:
            _log.info("No possible migration destination found for actor %s" %
                      self._name)
            # Try to enable/migrate actor again after a delay.
            get_calvinsys().scheduler_maintenance_wakeup(delay=True)

    @verify_status([STATUS.MIGRATABLE, STATUS.READY])
    def remove_migration_info(self, status):
        if status.status != 200:
            self._migration_info = None
            # FIXME: destroy() in actormanager.py was called before trying to migrate.
            #        Need to make the actor runnable again before transition to DENIED.
            #self.fsm.transition_to(Actor.STATUS.DENIED)

    def is_shadow(self):
        return False
コード例 #10
0
from calvin.utilities.utils import enum
# Only TEMPORARY, TERMINATE and EXHAUST are used on highest level in actor and port manager
# TEMPORARY is used during migration
# TERMINATE is used for dereplication without preserving tokens
# EXHAUST is used for dereplication while preserving tokens
# The different exhaust disconnects are based on a higher level exhaust value
# EXHAUST and EXHAUST_PEER are used at actor port, endpoint and connection level
# EXHAUST_INPORT, EXHAUST_OUTPORT, EXHAUST_PEER_SEND, EXHAUST_PEER_RECV are used at queue level
DISCONNECT = enum('TEMPORARY', 'TERMINATE', 'EXHAUST', 'EXHAUST_PEER',
                  'EXHAUST_INPORT', 'EXHAUST_OUTPORT', 'EXHAUST_PEER_RECV', 'EXHAUST_PEER_SEND')
コード例 #11
0
ファイル: common.py プロジェクト: vmillnert/calvin-base
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from calvin.utilities.utils import enum

COMMIT_RESPONSE = enum('handled', 'unhandled', 'invalid')

class QueueNone(object):
    def __init__(self):
        super(QueueNone, self).__init__()
        self.state = {'queuetype': 'none'}

    def _state(self):
        return self.state

    def _set_state(self, state):
        self.state = state

    def __str__(self):
        return "QueueNone: %s" % str(self.state)
コード例 #12
0
from calvin.utilities.utils import enum
# Only TEMPORARY, TERMINATE and EXHAUST are used on highest level in actor and port manager
# TEMPORARY is used during migration
# TERMINATE is used for dereplication without preserving tokens
# EXHAUST is used for dereplication while preserving tokens
# The different exhaust disconnects are based on a higher level exhaust value
# EXHAUST and EXHAUST_PEER are used at actor port, endpoint and connection level
# EXHAUST_INPORT, EXHAUST_OUTPORT, EXHAUST_PEER_SEND, EXHAUST_PEER_RECV are used at queue level
DISCONNECT = enum('TEMPORARY', 'TERMINATE', 'EXHAUST', 'EXHAUST_PEER',
                  'EXHAUST_INPORT', 'EXHAUST_OUTPORT', 'EXHAUST_PEER_RECV',
                  'EXHAUST_PEER_SEND')
コード例 #13
0
# -*- coding: utf-8 -*-

# Copyright (c) 2017 Ericsson AB
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from calvin.utilities.utils import enum

REPLICATION_STATUS = enum('UNUSED', 'READY', 'REPLICATING', 'DEREPLICATING')
PRE_CHECK = enum('NO_OPERATION', 'SCALE_OUT', 'SCALE_OUT_KNOWN', 'SCALE_IN')
コード例 #14
0
from calvin.utilities import calvinconfig
from calvin.utilities import calvinlogger

_log = calvinlogger.get_logger(__name__)


STATE = enum("NEW",  # Object is initiated but not loaded or started.
             "CONFIGURED",  # An acceptable configuration is loaded.
             "LISTENING",  # Listen for request or response.
             "TRANSMIT",  # Transmit a request or response.
             "SATISFIED",  # Required security discoveries established.
             "CSR_GENERATED",  # CSR has been generated.
             "CSR_REQUESTED",  # CSR has been requested.
             "CERTIFICATE_RECEIVED",  # Certificate has been received.
             "CERTIFICATE_VALID",  # Certificate validly signed by CA.
             "CA_ACCEPTED_CONFIGURATION",  # CAcert meets config requirements.
             "CERTIFICATE_ACCEPTED_CONFIGURATION",  # Certificate meets req.
             "CERTIFICATE_STORED",  # Certificate stored.
             "CA_GENERATED",  # CA cert generated.
             "CSR_RECEIVED",  # CSR has been received.
             "CSR_ACCEPTED",  # CSR meets config requirements.
             "CSR_STORED",  # CSR stored in new csr location
             "CSR_SIGNED",  # CSR have been signed by CA.
             "CSR_ALREDY_SIGNED",  # The requested CSR already signed.
             "CERTIFICATE_GENERATED")  # Certificate generated by CA





# Exceptions