def _getEndpointConnection(self, epA, epB): """ Internally used method to get the connection between two endpoints. @param epX: The endpoint which is part of the connection that should be retrieved. @type epX: rce.core.network.Endpoint @return: Connection between the two endpoints. @rtype: rce.core.network.EndpointConnection """ if epA not in self._endpoints or epB not in self._endpoints: raise InternalError('Endpoint is not part of this network.') if epA == epB: return epA.getLoopback() else: connectionsA = self._endpoints[epA] connectionsB = self._endpoints[epB] candidates = connectionsA.intersection(connectionsB) if candidates: if len(candidates) != 1: raise InternalError('There are more than one possible ' 'endpoint connections.') return candidates.pop() else: connection = EndpointConnection(epA, epB) connectionsA.add(connection) connectionsB.add(connection) return connection
def remote_createTunnel(self, name, targetIP): """ Create a new GRE Tunnel. @param name: Unique name of the network group. @type name: str @param targetIP: Target IP for the GRE Tunnel. @type targetIP: str @return: Exit status of command. @rtype: twisted.internet.defer.Deferred """ if name not in self._bridges: raise InternalError('Bridge does not exist.') key = (name, targetIP) if key in self._uid: raise InternalError('Tunnel already exists.') while 1: uid = randomString(self._UID_LEN) if uid not in self._uid.itervalues(): break self._uid[key] = uid port = 'gre-{0}'.format(uid) return execute( ('/usr/bin/ovs-vsctl', 'add-port', 'br-{0}'.format(name), port, '--', 'set', 'interface', port, 'type=gre', 'options:remote_ip={0}'.format(targetIP)), reactor=self._reactor)
def send(self, msg, msgID, protocol, remoteID): """ This method is used to send a message to the endpoint. Don't overwrite this method; instead overwrite the method _send. If the interface does not overwrite the method _send, it is assumed that the interface does not support this action and an InternalError is raised when send is called. @param msg: Message which should be sent in serialized form. @type msg: str @param msgID: Message ID which is used to match response message. @type msgID: str @param protocol: Protocol instance through which the message was sent. @type protocol: rce.slave.protocol._Protocol @param remoteID: Unique ID of the Interface which sent the message. @type remoteID: uuid.UUID """ if not self._ready: raise InternalError('Interface is not ready to send a message.') try: if remoteID not in self._protocols[protocol]: raise KeyError except KeyError: log.msg('Received message dropped, because interface does not ' 'expected the message.') self._send(msg, msgID, protocol, remoteID)
def _send(self, msg, msgID, protocol, remoteID): """ Convert a ROS message into a JSON encoded message. @param msg: Received ROS message in serialized form. @type msg: str @param msgID: Unique ID to identify the message. @type msgID: str @param protocol: Protocol instance through which the message was sent. @type protocol: rce.slave.protocol._Protocol @param remoteID: Unique ID of the Interface which sent the message. @type remoteID: uuid.UUID """ if not self._outputMsgCls: raise InternalError('This converter can not handle outgoing ' 'messages.') rosMsg = self._outputMsgCls() rosMsg.deserialize(msg) try: jsonMsg = self._converter.encode(rosMsg) except (TypeError, ValueError) as e: raise ConversionError(str(e)) self._sendToClient(jsonMsg, msgID, protocol, remoteID)
def requestAvatar(self, avatarId, mind, *interfaces): """ Returns Avatar for slave processes of the cloud engine. Implementation for IRealm """ if IPerspective not in interfaces: raise NotImplementedError('RoboEarthCloudEngine only ' 'handles IPerspective.') # There are three possible roles (=avatarId): # 'container', 'robot', and 'environment' if avatarId == 'container': machine = self._balancer.createMachine(mind[0], mind[1]) avatar = MachineAvatar(machine, self._balancer) detach = lambda: avatar.logout() print('Connection to Container process established.') elif avatarId == 'robot': endpoint = RobotEndpoint(self._network, self._distributor, self._port) endpoint.callback(mind) avatar = RobotEndpointAvatar(self, endpoint) detach = lambda: avatar.logout() print('Connection to Robot process established.') elif avatarId == 'environment': endpoint = self._pendingContainer.pop(mind[1]) endpoint.callback(mind[0]) avatar = EnvironmentEndpointAvatar(self, endpoint) detach = lambda: avatar.logout() print('Connection to Environment process established.') else: raise InternalError('Invalid avatar ID received.') return IPerspective, avatar, detach
def remote_createInterface(self, uid, iType, msgType, addr): """ Create an Interface object in the namespace and therefore in the endpoint. @param uid: Unique ID which is used to identify the interface in the internal communication. @type uid: str @param iType: Type of the interface encoded as an integer. Refer to rce.slave.interface.Types for more information. @type IType: int @param clsName: Message type/Service type consisting of the package and the name of the message/service, i.e. 'std_msgs/Int32'. @type clsName: str @param addr: Unique address which is used to identify the interface in the external communication. @type addr: str @return: New Interface instance. @rtype: rce.slave.interface.Interface """ try: cls = self._map[iType] except KeyError: raise InternalError('Interface type is not supported by this ' 'namespace.') return cls(self, UUID(bytes=uid), msgType, addr)
def createContainer(self, userID, data): """ Callback for User instance to create a new Container object in a container process. @param userID: UserID of the user who created the container. @type userID: str @param data: Extra data which is used to configure the container. @param data: dict @return: New Namespace and Container instance. @rtype: (rce.core.environment.Environment, rce.core.container.Container) (subclasses of rce.core.base.Proxy) """ while 1: uid = uuid4().hex if uid not in self._pendingContainer: break try: container = self._balancer.createContainer(uid, userID, data) except ContainerProcessError: # TODO: What should we do here? raise InternalError('Container can not be created.') endpoint = EnvironmentEndpoint(self._network, container) self._pendingContainer[uid] = endpoint return endpoint.createNamespace(), container
def receive(self, clsName, msgID, msg): """ Convert a JSON encoded message into a ROS message. @param clsName: Message/Service type of the received message. @type clsName: str @param msgID: Identifier which is used to match request / response message. @type msgID: str @param msg: JSON compatible message which should be processed. @type msg: dict """ if not self._inputMsgCls: raise InternalError('This converter can not handle incoming' ' messages.') if clsName != self._clsName: raise InvalidResoureName('Sent message type does not match the ' 'used message type for this interface.') try: msg = self._converter.decode(self._inputMsgCls, msg) except (TypeError, ValueError) as e: raise ConversionError(str(e)) buf = StringIO() msg.serialize(buf) msg = buf.getvalue() self._receive(msg, msgID)
def returnNr(self, nr): """ Callback for Container to return a container number when it is no longer in use such that it can be reused. """ if nr in self._nrs: raise InternalError('Number was never rented out.') self._nrs.add(nr)
def createEnvironment(self, _): """ Create the Environment namespace. """ if self._namespaces: raise InternalError('The environment can have only one namespace ' 'at a time.') environment = Environment(self) return self._avatar.callRemote('setupNamespace', environment)
def registerIAASHook(self, hook): """ Register an IAAS Hook object. # TODO: Add doc """ if not isinstance(hook, IaasHook): raise InternalError('IAAS hook has to be a subclass of ' 'rce.util.iaas.IaasHook.') self._iaas = hook
def assignMachine(self, machine): """ # TODO: Add doc """ if self._machine: raise InternalError('Can not assign the same container multiple ' 'times.') self._machine = machine self._group.registerContainer(self) machine.registerContainer(self)
def destroyMachine(self, machine): """ Destroy a Machine object. @param machine: Machine instance which should be destroyed. @type machine: rce.core.machine.Machine """ try: self._machines.remove(machine) except KeyError: raise InternalError('Tried to remove a non existent machine.') machine.destroy()
def createNamespace(self): """ Create a Environment object in the environment endpoint. @return: New Environment instance. @rtype: rce.master.environment.Environment (subclass of rce.core.base.Proxy) """ if self._namespaces: raise InternalError('Can not have more than one namespace ' 'in an Environment endpoint at a time.') return Environment(self)
def addCustomConverter(self, converter): """ Register a new custom Converter. @raise: rce.error.InternalError, rce.util.interfaces.InterfaceError """ verifyClass(ICustomROSConverter, converter) if converter.MESSAGE_TYPE in self._customTypes: raise InternalError('There are multiple Converters given for ' 'message type "{0}".'.format( converter.MESSAGE_TYPE)) try: pkg, name = package_resource_name(converter.MESSAGE_TYPE) except ValueError: raise InternalError('msg type is not valid. Has to be of the from ' 'pkg/msg, i.e. std_msgs/Int8.') self._customTypes[converter.MESSAGE_TYPE] = (converter, self._loader.loadMsg(pkg, name))
def removeCustomConverter(self, msgType): """ Unregister a custom Converter. @param msgType: Message type of ROS message as a string, i.e. 'std_msgs/Int8', for which the converter should be removed. @type msgType: str """ try: del self._customTypes[msgType] except KeyError: InternalError('Tried to remove a custom converter which was ' 'never added.')
def getInterfaceConnection(self, interface, protocol): """ Get the connection between an interface and a protocol. @param interface: Interface which belongs to this endpoint and which is on one side of the connection. @type interface: rce.core.network.Interface @param protocol: Protocol which belongs to this endpoint and which is on one side of the connection. @type protocol: rce.core.network.Protocol @return: Connection between the interface and the protocol. @rtype: rce.core.network.InterfaceConnection """ try: connectionI = self._interfaces[interface] except KeyError: raise InternalError('Interface does not belong to this endpoint.') try: connectionP = self._protocols[protocol] except KeyError: raise InternalError('Protocol does not belong to this endpoint.') candidates = connectionP.intersection(connectionI) if candidates: if len(candidates) != 1: raise InternalError('There are more than one possible ' 'interface-protocol connections.') return candidates.pop() else: connection = InterfaceConnection(interface, protocol) connectionI.add(connection) connectionP.add(connection) return connection
def remote_destroyTunnel(self, name, targetIP): """ Destroy a GRE Tunnel. @param name: Unique name of the network group. @type name: str @param targetIP: Target IP for the GRE Tunnel. @type targetIP: str @return: Exit status of command. @rtype: twisted.internet.defer.Deferred """ if name not in self._bridges: raise InternalError('Bridge does not exist.') key = (name, targetIP) if key not in self._uid: raise InternalError('Tunnel deos not exist.') return execute(('/usr/bin/ovs-vsctl', 'del-port', 'gre-{0}'.format( self._uid.pop(key))), reactor=self._reactor)
def remote_destroyBridge(self, name): """ Destroy a OVS Bridge. @param name: Unique name of the network group. @type name: str @return: Exit status of command. @rtype: twisted.internet.defer.Deferred """ if name not in self._bridges: raise InternalError('Bridge does not exist.') self._bridges.remove(name) return execute(('/usr/bin/ovs-vsctl', 'del-br', 'br-{0}'.format(name)), reactor=self._reactor)
def remote_createBridge(self, name): """ Create a new OVS Bridge. @param name: Unique name of the network group. @type name: str @return: Exit status of command. @rtype: twisted.internet.defer.Deferred """ if name in self._bridges: raise InternalError('Bridge already exists.') self._bridges.add(name) return execute(('/usr/bin/ovs-vsctl', '--', '--may-exist', 'add-br', 'br-{0}'.format(name)), reactor=self._reactor)
def _validate(self, result): """ Internally used method which is part of a callback chain. Its task is to verify that both sides of the connection could be authenticated. In case both signal success the corresponding protocols are registered. """ ((serverAuth, clientProtocol), (clientAuth, serverProtocol)) = result if not (serverAuth and clientAuth): # There was a problem in authenticating the connection # TODO: What should we do here? return Failure( InternalError('Connection could not be ' 'authenticated.')) self._serverProtocol.callback(serverProtocol) self._clientProtocol.callback(clientProtocol)
def sendMessage(self, interface, msg, msgID, remoteID=None): assert self._initialized uid = interface.UID.bytes assert len(uid) == 16 try: idLen = self._MSG_ID_STRUCT.pack(len(msgID)) except struct.error: raise InternalError('Message ID is too long.') if remoteID: flag = self._TRUE rmtID = remoteID.bytes assert len(rmtID) == 16 else: flag = self._FALSE rmtID = '' self.sendString(''.join((flag, rmtID, uid, idLen, msgID, msg)))
def requestURL(self, userID): """ Callback for Robot resource to retrieve the location of the Robot process to which a WebSocket connection should be established. @param userID: User ID under which the robot will login. (Can be used to do optimizations in distributing the load.) @type userID: str @return: The IP address of Robot process to which a WebSocket connection should be established. (type: str) @rtype: twisted.internet.defer.Deferred """ try: location = self._distributor.getNextLocation() except RobotProcessError: # TODO: What should we do here? raise InternalError('Robot can not be created.') return location.getWebsocketAddress()
def createMachine(self, ref, data): """ Create a new Machine object, which can be used to create new containers. @param ref: Remote reference to the ContainerClient in the container process. @type ref: twisted.spread.pb.RemoteReference @param data: Data about the machine @type data: dict @return: New Machine instance. @rtype: rce.core.machine.Machine """ machine = Machine(ref, data, self) if machine in self._machines: raise InternalError( 'Tried to add the same machine multiple times.') self._machines.add(machine) return machine
def getProtocol(self, endpoint): """ Get the protocol which is part of this connection and belongs to the given endpoint. @param endpoint: Endpoint to which the protocol has to belong. @type endpoint: rce.core.network.Endpoint @return: Protocol which belongs to the endpoint and is part of this connection. @rtype: rce.core.network.Protocol (subclass of rce.core.base.Proxy) """ if not (self._serverProtocol and self._serverEndpoint and self._clientProtocol and self._clientEndpoint): raise ConnectionError('Endpoint connection is dead.') if self._serverEndpoint == endpoint: return self._serverProtocol elif self._clientEndpoint == endpoint: return self._clientProtocol else: raise InternalError('The endpoint is not part of this connection.')
def _getAddress(self, result): """ Internally used method which is part of a callback chain. Its task is to verify that both endpoints are ready for the connection attempt. In case both signal readiness the address of the designated server endpoint is retrieved. @param result: Response of the DeferredList containing the Deferreds of the 'prepareConnection' calls. @return: Address of the endpoint's internal communication server. @rtype: twisted.internet.address.IPv4Address """ ((serverReady, _), (clientReady, _)) = result if not (serverReady and clientReady): # There was a problem in making the server/client ready for the # connection attempt # TODO: What should we do here? return Failure( InternalError('Server/Client could not be prepared ' 'for connection attempt.')) return self._serverEndpoint.getAddress()
def _send(self, msg, msgID, protocol, remoteID): raise InternalError('Interface does not support sending of a message.')
def remote_connect(self, protocol, remoteID): if self._protocols: raise InternalError('Can not register more than one interface ' 'at a time with a Service-Provider.') return _ROSInterfaceBase.remote_connect(self, protocol, remoteID)