Пример #1
0
def launch_listener():  # pragma: no cover
    def notify_driver_event(evt):
        print("notify_driver_event received: %s" % str(evt.event_instance))

    print 'launching listener, port=%d ...' % HTTP_SERVER_PORT
    oms_event_listener = OmsEventListener("dummy_plat_id", notify_driver_event)
    oms_event_listener.keep_notifications()
    oms_event_listener.start_http_server(host='', port=HTTP_SERVER_PORT)
    print 'listener launched'
    return oms_event_listener
Пример #2
0
def launch_listener():  # pragma: no cover
    def notify_driver_event(evt):
        print("notify_driver_event received: %s" % str(evt.event_instance))

    print 'launching listener, port=%d ...' % HTTP_SERVER_PORT
    oms_event_listener = OmsEventListener("dummy_plat_id", notify_driver_event)
    oms_event_listener.keep_notifications()
    oms_event_listener.start_http_server(host='', port=HTTP_SERVER_PORT)
    print 'listener launched'
    return oms_event_listener
    def __init__(self, pnode, event_callback):
        """
        Creates an RSNPlatformDriver instance.

        @param pnode           Root PlatformNode defining the platform network
                               rooted at this platform.
        @param event_callback  Listener of events generated by this driver
        """
        PlatformDriver.__init__(self, pnode, event_callback)

        # CIOMSClient instance created by connect() and destroyed by disconnect():
        self._rsn_oms = None

        # external event listener: we can instantiate this here as the
        # actual http server is started via corresponding method.
        self._event_listener = OmsEventListener(self._platform_id, self._notify_driver_event)
Пример #4
0
    def __init__(self, pnode, evt_recv):
        """
        Creates an RSNPlatformDriver instance.

        @param pnode     Root PlatformNode defining the platform network rooted at
                         this platform.
        @param evt_recv  Listener of events generated by this driver
        """
        PlatformDriver.__init__(self, pnode, evt_recv)

        # CIOMSClient instance created by connect()
        self._rsn_oms = None

        # external event listener: we can instantiate this here as the the
        # actual http server is started via corresponding method.
        self._event_listener = OmsEventListener(self._notify_driver_event)
class RSNPlatformDriver(PlatformDriver):
    """
    The main RSN OMS platform driver class.
    """

    def __init__(self, pnode, event_callback):
        """
        Creates an RSNPlatformDriver instance.

        @param pnode           Root PlatformNode defining the platform network
                               rooted at this platform.
        @param event_callback  Listener of events generated by this driver
        """
        PlatformDriver.__init__(self, pnode, event_callback)

        # CIOMSClient instance created by connect() and destroyed by disconnect():
        self._rsn_oms = None

        # external event listener: we can instantiate this here as the
        # actual http server is started via corresponding method.
        self._event_listener = OmsEventListener(self._platform_id, self._notify_driver_event)

    def _filter_capabilities(self, events):
        """
        """
        events_out = [x for x in events if RSNPlatformDriverCapability.has(x)]
        return events_out

    def validate_driver_configuration(self, driver_config):
        """
        Driver config must include 'oms_uri' entry.
        """
        if not 'oms_uri' in driver_config:
            log.error("'oms_uri' not present in driver_config = %s", driver_config)
            raise PlatformDriverException(msg="driver_config does not indicate 'oms_uri'")

    def configure(self, driver_config):
        """
        Nothing special done here, only calls super.configure(driver_config)

        @param driver_config with required 'oms_uri' entry.
        """
        PlatformDriver.configure(self, driver_config)
        self._construct_resource_schema()

    def _construct_resource_schema(self):
        """
        """
        
        parameters = deepcopy(self._param_dict)
        ports_dict = self._driver_config.get('ports',{})
        ports = []
        for k,v in ports_dict.iteritems():
            ports.append(v['port_id'])
        for k,v in parameters.iteritems():
            read_write = v.get('read_write', None)
            if read_write == 'write':
                v['visibility'] = 'READ_WRITE'
            else:
                v['visibility'] = 'READ_ONLY'
                
        commands = {}
        commands[RSNPlatformDriverEvent.CONNECT_INSTRUMENT] = \
            {
                "display_name" : "Connect Instrument",
                "description" : "Connect an instrument to the platform.",
                "args" : [], 
                "kwargs" : {
                    'port_id' : {
                        "required" : True,
                        "type" : "int",
                        "valid_values" : ports
                        },
                    'instrument_id' : {
                        "required" : True,
                        "type" : "str"
                        },
                    'attributes' : {
                        "required" : True,
                        "type" : "dict"
                        }                    
                }
            }
        commands[RSNPlatformDriverEvent.DISCONNECT_INSTRUMENT] = \
            {
                "display_name" : "Disconnect Instrument",
                "description" : "Disconnect an instrument from the platform.",
                "args" : [], 
                "kwargs" : {
                    'port_id' : {
                        "required" : True,
                        "type" : "int",
                        "valid_values" : ports
                        },
                    'instrument_id' : {
                        "required" : True,
                        "type" : "str"
                        }
                }
            }
        commands[RSNPlatformDriverEvent.TURN_ON_PORT] = \
            {
                "display_name" : "Port Power On",
                "description" : "Activate port power.",
                "args" : [],
                "kwargs" : {
                       'port_id' : {
                            "required" : True,
                            "type" : "int",
                            "valid_values" : ports
                        }
                }
                     
            }
        commands[RSNPlatformDriverEvent.TURN_OFF_PORT] = \
            {
                "display_name" : "Port Power Off",
                "description" : "Deactivate port power.",
                "args" : [],
                "kwargs" : {
                       'port_id' : {
                            "required" : True,
                            "type" : "int",
                            "valid_values" : ports
                        }
                }
            }
        commands[RSNPlatformDriverEvent.CHECK_SYNC] = \
            {
                "display_name" : "Check Platform Hierarchy",
                "description" : "Verify the platform hierarchy is consistent with OMS.",
                "args" : [],
                "kwargs" : {}
            }       
        self._resource_schema['parameters'] = parameters
        self._resource_schema['commands'] = commands
                
    def _assert_rsn_oms(self):
        assert self._rsn_oms is not None, "_rsn_oms object required (created via connect() call)"

    def ping(self):
        """
        Verifies communication with external platform returning "PONG" if
        this verification completes OK.

        @retval "PONG" iff all OK.
        @raise PlatformConnectionException Cannot ping external platform or
               got unexpected response.
        """
        log.debug("%r: pinging OMS...", self._platform_id)
        self._assert_rsn_oms()
        try:
            retval = self._rsn_oms.hello.ping()
        except Exception as e:
            raise PlatformConnectionException(msg="Cannot ping: %s" % str(e))

        if retval is None or retval.upper() != "PONG":
            raise PlatformConnectionException(msg="Unexpected ping response: %r" % retval)

        log.debug("%r: ping completed: response: %s", self._platform_id, retval)

        return "PONG"

    def connect(self):
        """
        Creates an CIOMSClient instance, does a ping to verify connection,
        and starts event dispatch.
        """
        # create CIOMSClient:
        oms_uri = self._driver_config['oms_uri']
        log.debug("%r: creating CIOMSClient instance with oms_uri=%r",
                  self._platform_id, oms_uri)
        self._rsn_oms = CIOMSClientFactory.create_instance(oms_uri)
        log.debug("%r: CIOMSClient instance created: %s",
                  self._platform_id, self._rsn_oms)

        # ping to verify connection:
        self.ping()

        # start event dispatch:
        self._start_event_dispatch()

    def disconnect(self):
        """
        Stops event dispatch and destroys the CIOMSClient instance.
        """
        self._stop_event_dispatch()
        CIOMSClientFactory.destroy_instance(self._rsn_oms)
        self._rsn_oms = None
        log.debug("%r: CIOMSClient instance destroyed", self._platform_id)

    def get_metadata(self):
        """
        """
        self._assert_rsn_oms()
        try:
            retval = self._rsn_oms.config.get_platform_metadata(self._platform_id)
        except Exception as e:
            raise PlatformConnectionException(msg="Cannot get_platform_metadata: %s" % str(e))

        log.debug("get_platform_metadata = %s", retval)

        if not self._platform_id in retval:
            raise PlatformException("Unexpected: response does not include "
                                    "requested platform '%s'" % self._platform_id)

        md = retval[self._platform_id]
        return md

    def get_subplatform_ids(self):
        """
        Gets the IDs of my sub-platforms.
        """
        return self._pnode.subplatforms.keys()

    def get_attribute_values(self, attrs):
        """
        """
        log.debug("get_attribute_values: attrs=%s", attrs)

        if not isinstance(attrs, (list, tuple)):
            raise PlatformException('get_attribute_values: attrs argument must be a '
                                    'list [(attrName, from_time), ...]. Given: %s', attrs)

        self._assert_rsn_oms()

        # convert the ION system time from_time to NTP, as this is the time
        # format used by the RSN OMS interface:
        attrs_ntp = [(attr_id, ion_ts_2_ntp(from_time))
                     for (attr_id, from_time) in attrs]

        try:
            retval = self._rsn_oms.attr.get_platform_attribute_values(self._platform_id,
                                                                      attrs_ntp)
        except Exception as e:
            raise PlatformConnectionException(msg="Cannot get_platform_attribute_values: %s" % str(e))

        if not self._platform_id in retval:
            raise PlatformException("Unexpected: response does not include "
                                    "requested platform '%s'" % self._platform_id)

        attr_values = retval[self._platform_id]

        # reported timestamps are already in NTP. Just return the dict:
        return attr_values

    def _validate_set_attribute_values(self, attrs):
        """
        Does some pre-validation of the passed values according to the
        definition of the attributes.

        NOTE: We don't check everything here, just some basics.
        TODO determine appropriate validations at this level.
        Note that the basic checks here follow what the OMS system
        will do if we just send the request directly to it. So,
        need to determine what exactly should be done on the CI side.

        @param attrs

        @return dict of errors for the offending attribute names, if any.
        """
        # TODO determine appropriate validations at this level.

        # get definitions to verify the values against
        attr_defs = self._get_platform_attributes()

        if log.isEnabledFor(logging.DEBUG):
            log.debug("validating passed attributes: %s against defs %s", attrs, attr_defs)

        # to collect errors, if any:
        error_vals = {}
        for attr_name, attr_value in attrs:

            attr_def = attr_defs.get(attr_name, None)

            if log.isEnabledFor(logging.DEBUG):
                log.debug("validating %s against %s", attr_name, str(attr_def))

            if not attr_def:
                error_vals[attr_name] = InvalidResponse.ATTRIBUTE_ID
                log.warn("Attribute %s not in associated platform %s",
                         attr_name, self._platform_id)
                continue

            type_ = attr_def.get('type', None)
            units = attr_def.get('units', None)
            min_val = attr_def.get('min_val', None)
            max_val = attr_def.get('max_val', None)
            read_write = attr_def.get('read_write', None)
            group = attr_def.get('group', None)

            if "write" != read_write:
                error_vals[attr_name] = InvalidResponse.ATTRIBUTE_NOT_WRITABLE
                log.warn(
                    "Trying to set read-only attribute %s in platform %s",
                    attr_name, self._platform_id)
                continue

            #
            # TODO the following value-related checks are minimal
            #
            if type_ in ["float", "int"]:
                if min_val and float(attr_value) < float(min_val):
                    error_vals[attr_name] = InvalidResponse.ATTRIBUTE_VALUE_OUT_OF_RANGE
                    log.warn(
                        "Value %s for attribute %s is less than specified minimum "
                        "value %s in associated platform %s",
                        attr_value, attr_name, min_val,
                        self._platform_id)
                    continue

                if max_val and float(attr_value) > float(max_val):
                    error_vals[attr_name] = InvalidResponse.ATTRIBUTE_VALUE_OUT_OF_RANGE
                    log.warn(
                        "Value %s for attribute %s is greater than specified maximum "
                        "value %s in associated platform %s",
                        attr_value, attr_name, max_val,
                        self._platform_id)
                    continue

        return error_vals

    def set_attribute_values(self, attrs):
        """
        """
        log.debug("set_attribute_values: attrs = %s", attrs)

        self._assert_rsn_oms()

        error_vals = self._validate_set_attribute_values(attrs)
        if len(error_vals) > 0:
            # remove offending attributes for the request below
            attrs_dict = dict(attrs)
            for bad_attr_name in error_vals:
                del attrs_dict[bad_attr_name]

            # no good attributes at all?
            if len(attrs_dict) == 0:
                # just immediately return with the errors:
                return error_vals

            # else: update attrs with the good attributes:
            attrs = attrs_dict.items()

        # ok, now make the request to RSN OMS:
        try:
            retval = self._rsn_oms.attr.set_platform_attribute_values(self._platform_id,
                                                                      attrs)
        except Exception as e:
            raise PlatformConnectionException(msg="Cannot set_platform_attribute_values: %s" % str(e))

        log.debug("set_platform_attribute_values = %s", retval)

        if not self._platform_id in retval:
            raise PlatformException("Unexpected: response does not include "
                                    "requested platform '%s'" % self._platform_id)

        attr_values = retval[self._platform_id]

        # Note that the reported timestamps are in NTP.
        # (Timestamps indicate the time when the value was set for each attribute.)

        # ret_attr_values: dictionary to return, initialized with the error ones
        # determined above, if any:
        ret_attr_values = error_vals

        # add the info returned from RSN OMS:
        for attr_name, attr_val_ts in attr_values.iteritems():
            ret_attr_values[attr_name] = attr_val_ts

        log.debug("set_attribute_values: returning %s", ret_attr_values)

        return ret_attr_values

    def _verify_platform_id_in_response(self, response):
        """
        Verifies the presence of my platform_id in the response.

        @param response Dictionary returned by _rsn_oms

        @retval response[self._platform_id]
        """
        if not self._platform_id in response:
            msg = "unexpected: response does not contain entry for %r" % self._platform_id
            log.error(msg)
            raise PlatformException(msg=msg)

        if response[self._platform_id] == InvalidResponse.PLATFORM_ID:
            msg = "response reports invalid platform_id for %r" % self._platform_id
            log.error(msg)
            raise PlatformException(msg=msg)
        else:
            return response[self._platform_id]

    def _verify_port_id_in_response(self, port_id, dic):
        """
        Verifies the presence of port_id in the dic.

        @param port_id  The ID to verify
        @param dic Dictionary returned by _rsn_oms

        @return dic[port_id]
        """
        if not port_id in dic:
            msg = "unexpected: dic does not contain entry for %r" % port_id
            log.error(msg)
            raise PlatformException(msg=msg)

        if dic[port_id] == InvalidResponse.PORT_ID:
            msg = "%r: response reports invalid port_id for %r" % (
                                 self._platform_id, port_id)
            log.error(msg)
            raise PlatformException(msg=msg)
        else:
            return dic[port_id]

    def _verify_instrument_id_in_response(self, port_id, instrument_id, dic):
        """
        Verifies the presence of instrument_id in the dic.

        @param port_id        Used for error reporting
        @param instrument_id  The ID to verify
        @param dic            Dictionary returned by _rsn_oms

        @return dic[instrument_id]
        """
        if not instrument_id in dic:
            msg = "unexpected: dic does not contain entry for %r" % instrument_id
            log.error(msg)
            raise PlatformException(msg=msg)

        return dic[instrument_id]

    def connect_instrument(self, port_id, instrument_id, attributes):
        log.debug("%r: connect_instrument: port_id=%r instrument_id=%r attributes=%s",
                  self._platform_id, port_id, instrument_id, attributes)

        self._assert_rsn_oms()

        try:
            response = self._rsn_oms.instr.connect_instrument(self._platform_id,
                                                              port_id,
                                                              instrument_id,
                                                              attributes)
        except Exception as e:
            raise PlatformConnectionException(msg="Cannot connect_instrument: %s" % str(e))

        log.debug("%r: connect_instrument response: %s",
                  self._platform_id, response)

        dic_plat = self._verify_platform_id_in_response(response)
        port_dic = self._verify_port_id_in_response(port_id, dic_plat)
        instr_res = self._verify_instrument_id_in_response(port_id, instrument_id, port_dic)

        # update local image if instrument was actually connected in this call:
        if isinstance(instr_res, dict):
            attrs = instr_res
            instrumentNode = InstrumentNode(instrument_id, attrs)
            self._pnode.ports[port_id].add_instrument(instrumentNode)
            log.debug("%r: port_id=%s connect_instrument: local image updated: %s",
                      self._platform_id, port_id, instrument_id)

        return dic_plat  # note: return the dic for the platform

    def disconnect_instrument(self, port_id, instrument_id):
        log.debug("%r: disconnect_instrument: port_id=%r instrument_id=%r",
                  self._platform_id, port_id, instrument_id)

        self._assert_rsn_oms()

        try:
            response = self._rsn_oms.instr.disconnect_instrument(self._platform_id,
                                                                 port_id,
                                                                 instrument_id)
        except Exception as e:
            raise PlatformConnectionException(msg="Cannot disconnect_instrument: %s" % str(e))

        log.debug("%r: disconnect_instrument response: %s",
                  self._platform_id, response)

        dic_plat = self._verify_platform_id_in_response(response)
        port_dic = self._verify_port_id_in_response(port_id, dic_plat)
        instr_res = self._verify_instrument_id_in_response(port_id, instrument_id, port_dic)

        # update local image if instrument was actually disconnected in this call:
        if instr_res == NormalResponse.INSTRUMENT_DISCONNECTED:
            del self._pnode.ports[port_id].instruments[instrument_id]
            log.debug("%r: port_id=%s disconnect_instrument: local image updated: %s",
                      self._platform_id, port_id, instrument_id)

        return dic_plat  # note: return the dic for the platform

    def get_connected_instruments(self, port_id):
        log.debug("%r: get_connected_instruments: port_id=%s",
                  self._platform_id, port_id)

        self._assert_rsn_oms()

        try:
            response = self._rsn_oms.instr.get_connected_instruments(self._platform_id,
                                                                     port_id)
        except Exception as e:
            raise PlatformConnectionException(msg="Cannot get_connected_instruments: %s" % str(e))

        log.debug("%r: port_id=%r: get_connected_instruments response: %s",
                  self._platform_id, port_id, response)

        dic_plat = self._verify_platform_id_in_response(response)
        port_dic = self._verify_port_id_in_response(port_id, dic_plat)

        return dic_plat  # note: return the dic for the platform

    def turn_on_port(self, port_id):
        log.debug("%r: turning on port: port_id=%s",
                  self._platform_id, port_id)

        self._assert_rsn_oms()

        try:
            response = self._rsn_oms.port.turn_on_platform_port(self._platform_id,
                                                                port_id)
        except Exception as e:
            raise PlatformConnectionException(msg="Cannot turn_on_platform_port: %s" % str(e))

        log.debug("%r: turn_on_platform_port response: %s",
                  self._platform_id, response)

        dic_plat = self._verify_platform_id_in_response(response)
        self._verify_port_id_in_response(port_id, dic_plat)

        return dic_plat  # note: return the dic for the platform

    def turn_off_port(self, port_id):
        log.debug("%r: turning off port: port_id=%s",
                  self._platform_id, port_id)

        self._assert_rsn_oms()

        try:
            response = self._rsn_oms.port.turn_off_platform_port(self._platform_id,
                                                                 port_id)
        except Exception as e:
            raise PlatformConnectionException(msg="Cannot turn_off_platform_port: %s" % str(e))

        log.debug("%r: turn_off_platform_port response: %s",
                  self._platform_id, response)

        dic_plat = self._verify_platform_id_in_response(response)
        self._verify_port_id_in_response(port_id, dic_plat)

        return dic_plat  # note: return the dic for the platform

    ###############################################
    # External event handling:

    def _register_event_listener(self, url):
        """
        Registers given url for all event types.
        """
        try:
            result = self._rsn_oms.event.register_event_listener(url)
        except Exception as e:
            raise PlatformConnectionException(msg="Cannot register_event_listener: %s" % str(e))

        log.info("register_event_listener url=%r returned: %s", url, result)

    def _unregister_event_listener(self, url):
        """
        Unregisters given url for all event types.
        """
        try:
            result = self._rsn_oms.event.unregister_event_listener(url)
        except Exception as e:
            raise PlatformConnectionException(msg="Cannot unregister_event_listener: %s" % str(e))

        log.info("unregister_event_listener url=%r returned: %s", url, result)

    def _start_event_dispatch(self):
        """
        Starts the dispatch of events received from the platform network to do
        corresponding event notifications.
        """
        self._assert_rsn_oms()

        # start http server:
        self._event_listener.start_http_server()

        # then, register my listener:
        self._register_event_listener(self._event_listener.url)

        return "OK"

    def _stop_event_dispatch(self):
        """
        Stops the dispatch of events received from the platform network.
        """
        self._assert_rsn_oms()

        # unregister my listener:
        self._unregister_event_listener(self._event_listener.url)

        # then, stop http server:
        self._event_listener.stop_http_server()

        return "OK"

    ##############################################################
    # sync/checksum
    ##############################################################

    def get_external_checksum(self):
        """
        Returns the checksum of the external platform associated with this
        driver.

        @return SHA1 hash value as string of hexadecimal digits.
        """
        log.debug("%r: get_checksum...", self._platform_id)
        self._assert_rsn_oms()
        try:
            response = self._rsn_oms.config.get_checksum(self._platform_id)
        except Exception as e:
            raise PlatformConnectionException(msg="Cannot get_checksum: %s" % str(e))

        dic_plat = self._verify_platform_id_in_response(response)
        log.debug("%r: get_checksum... dic_plat=%s" % (self._platform_id, dic_plat))
        return dic_plat  # note: return the dic for the platform

    def _check_sync(self):
        """
        This will be the main operation related with checking that the
        information on this platform agent (and sub-platforms) is consistent
        with the information in the external network rooted at the
        corresponding platform, then publishing relevant notification events.

        For the moment, it only tries to do the following:
        - gets the checksum reported by the external platform
        - compares it with the local checksum
        - if equal ...
        - if different ...

        @todo complete implementation

        @return TODO
        """

        log.debug("%r: _check_sync: getting external checksum...", self._platform_id)

        external_checksum = self.get_external_checksum()
        local_checksum = self._pnode.compute_checksum()

        if external_checksum == local_checksum:
            result = "OK: checksum for platform_id=%r: %s" % (
                self._platform_id, local_checksum)
        else:
            result = "ERROR: different external and local checksums for " \
                     "platform_id=%r: %s != %s" % (self._platform_id,
                     external_checksum, local_checksum)

            # TODO - determine what sub-components are in disagreement
            # TODO - publish relevant event(s)

        log.debug("%r: _check_sync: result: %s", self._platform_id, result)

        return result

    ##############################################################
    # GET
    ##############################################################

    def get(self, *args, **kwargs):

        if 'attrs' in kwargs:
            attrs = kwargs['attrs']
            result = self.get_attribute_values(attrs)
            return result

        if 'subplatform_ids' in kwargs:
            result = self.get_subplatform_ids()
            return result

        if 'ports' in kwargs:
            result = self._get_ports()
            return result

        if 'connected_instruments' in kwargs:
            port_id = kwargs['connected_instruments']
            result = self.get_connected_instruments(port_id)
            return result

        if 'metadata' in kwargs:
            result = self.get_metadata()
            return result

        return super(RSNPlatformDriver, self).get(*args, **kwargs)

    ##############################################################
    # EXECUTE
    ##############################################################

    def execute(self, cmd, *args, **kwargs):
        """
        Executes the given command.

        @param cmd   command

        @return  result of the execution
        """

        if cmd == RSNPlatformDriverEvent.CHECK_SYNC:
            result = self._check_sync()

        elif cmd == RSNPlatformDriverEvent.TURN_ON_PORT:
            result = self.turn_on_port(*args, **kwargs)

        elif cmd == RSNPlatformDriverEvent.TURN_OFF_PORT:
            result = self.turn_off_port(*args, **kwargs)

        elif cmd == RSNPlatformDriverEvent.CONNECT_INSTRUMENT:
            result = self.connect_instrument(*args, **kwargs)

        elif cmd == RSNPlatformDriverEvent.DISCONNECT_INSTRUMENT:
            result = self.disconnect_instrument(*args, **kwargs)

        else:
            result = super(RSNPlatformDriver, self).execute(cmd, args, kwargs)

        return result

    def _get_ports(self):
        ports = {}
        for port_id, port in self._pnode.ports.iteritems():
            ports[port_id] = {'network': port.network,
                              'state':   port.state}
        log.debug("%r: _get_ports: %s", self._platform_id, ports)
        return ports

    ##############################################################
    # CONNECTED event handlers we add in this subclass
    ##############################################################

    def _handler_connected_connect_instrument(self, *args, **kwargs):
        """
        """
        if log.isEnabledFor(logging.TRACE):  # pragma: no cover
            log.trace("%r/%s args=%s kwargs=%s" % (
                      self._platform_id, self.get_driver_state(),
                      str(args), str(kwargs)))

        port_id = kwargs.get('port_id', None)
        if port_id is None:
            raise FSMError('connect_instrument: missing port_id argument')

        instrument_id = kwargs.get('instrument_id', None)
        if instrument_id is None:
            raise FSMError('connect_instrument: missing instrument_id argument')

        attributes = kwargs.get('attributes', None)
        if attributes is None:
            raise FSMError('connect_instrument: missing attributes argument')

        try:
            result = self.connect_instrument(port_id, instrument_id, attributes)
            return None, result

        except PlatformConnectionException as e:
            return self._connection_lost(RSNPlatformDriverEvent.CONNECT_INSTRUMENT,
                                         args, kwargs, e)

    def _handler_connected_disconnect_instrument(self, *args, **kwargs):
        """
        """
        if log.isEnabledFor(logging.TRACE):  # pragma: no cover
            log.trace("%r/%s args=%s kwargs=%s" % (
                      self._platform_id, self.get_driver_state(),
                      str(args), str(kwargs)))

        port_id = kwargs.get('port_id', None)
        if port_id is None:
            raise FSMError('disconnect_instrument: missing port_id argument')

        instrument_id = kwargs.get('instrument_id', None)
        if instrument_id is None:
            raise FSMError('disconnect_instrument: missing instrument_id argument')

        try:
            result = self.disconnect_instrument(port_id, instrument_id)
            next_state = None

        except PlatformConnectionException as e:
            return self._connection_lost(RSNPlatformDriverEvent.DISCONNECT_INSTRUMENT,
                                         args, kwargs, e)

        return next_state, result

    def _handler_connected_turn_on_port(self, *args, **kwargs):
        """
        """
        if log.isEnabledFor(logging.TRACE):  # pragma: no cover
            log.trace("%r/%s args=%s kwargs=%s" % (
                      self._platform_id, self.get_driver_state(),
                      str(args), str(kwargs)))

        port_id = kwargs.get('port_id', None)
        if port_id is None:
            raise FSMError('turn_on_port: missing port_id argument')

        try:
            result = self.turn_on_port(port_id)
            return None, result

        except PlatformConnectionException as e:
            return self._connection_lost(RSNPlatformDriverEvent.TURN_ON_PORT,
                                         args, kwargs, e)

    def _handler_connected_turn_off_port(self, *args, **kwargs):
        """
        """
        if log.isEnabledFor(logging.TRACE):  # pragma: no cover
            log.trace("%r/%s args=%s kwargs=%s" % (
                      self._platform_id, self.get_driver_state(),
                      str(args), str(kwargs)))

        port_id = kwargs.get('port_id', None)
        if port_id is None:
            raise FSMError('turn_off_port: missing port_id argument')

        try:
            result = self.turn_off_port(port_id)
            return None, result

        except PlatformConnectionException as e:
            return self._connection_lost(RSNPlatformDriverEvent.TURN_OFF_PORT,
                                         args, kwargs, e)

    def _handler_connected_check_sync(self, *args, **kwargs):
        """
        """
        if log.isEnabledFor(logging.TRACE):  # pragma: no cover
            log.trace("%r/%s args=%s kwargs=%s" % (
                      self._platform_id, self.get_driver_state(),
                      str(args), str(kwargs)))

        try:
            result = self._check_sync()
            return None, result

        except PlatformConnectionException as e:
            return self._connection_lost(RSNPlatformDriverEvent.CHECK_SYNC,
                                         args, kwargs, e)

    ##############################################################
    # RSN Platform driver FSM setup
    ##############################################################

    def _construct_fsm(self,
                       states=RSNPlatformDriverState,
                       events=RSNPlatformDriverEvent,
                       enter_event=RSNPlatformDriverEvent.ENTER,
                       exit_event=RSNPlatformDriverEvent.EXIT):
        """
        """
        log.debug("constructing RSN platform driver FSM")

        super(RSNPlatformDriver, self)._construct_fsm(states, events,
                                                      enter_event, exit_event)

        # CONNECTED state event handlers we add in this class:
        self._fsm.add_handler(PlatformDriverState.CONNECTED, RSNPlatformDriverEvent.CONNECT_INSTRUMENT, self._handler_connected_connect_instrument)
        self._fsm.add_handler(PlatformDriverState.CONNECTED, RSNPlatformDriverEvent.DISCONNECT_INSTRUMENT, self._handler_connected_disconnect_instrument)
        self._fsm.add_handler(PlatformDriverState.CONNECTED, RSNPlatformDriverEvent.TURN_ON_PORT, self._handler_connected_turn_on_port)
        self._fsm.add_handler(PlatformDriverState.CONNECTED, RSNPlatformDriverEvent.TURN_OFF_PORT, self._handler_connected_turn_off_port)
        self._fsm.add_handler(PlatformDriverState.CONNECTED, RSNPlatformDriverEvent.CHECK_SYNC, self._handler_connected_check_sync)