def _started(self, sender, **kwargs): # Created a link to the control agent on this platform. self._control_connection = Connection(address=self.core.address, peer='control') _log.debug('Querying router for addresses and serverkey.') q = Query(self.core) self._external_addresses = q.query('addresses').get(timeout=2) _log.debug('External addresses are: {}'.format( self._external_addresses)) self._local_serverkey = q.query('serverkey').get(timeout=2) _log.debug('serverkey is: {}'.format(self._local_serverkey)) vc_http_address = q.query('volttron-central-address').get(timeout=2) _log.debug('vc address is {}'.format(vc_http_address)) self._local_instance_name = q.query('instance-name').get(timeout=2) _log.debug('instance-name is {}'.format(self._local_instance_name)) if vc_http_address is not None: parsed = urlparse.urlparse(vc_http_address) if parsed.scheme in ('https', 'http'): self._volttron_central_http_address = vc_http_address elif parsed.scheme == 'ipc': self._volttron_central_ipc_address = vc_http_address elif parsed.scheme == 'tcp': self._volttron_central_tcp_address = vc_http_address vc_serverkey = q.query('volttron-central-serverkey').get( timeout=2) if vc_serverkey is None: raise ValueError( 'volttron-central-serverkey is not set with tcp address' ) self._volttron_central_serverkey = vc_serverkey else: raise ValueError('invalid scheme for volttron-central-address') self._agent_started = True _log.debug('Starting the period registration attempts.') # Start the process of attempting registration with a VOLTTRON central. self._periodic_attempt_registration() try: _log.debug('Starting stats publisher.') self._start_stats_publisher() except ValueError as e: _log.error(e)
def build_connection(self, peer=None, address=None, identity=None, publickey=None, secretkey=None, serverkey=None, capabilities=[], **kwargs): self.logit('Building connection to {}'.format(peer)) self.allow_all_connections() if address is None: self.logit( 'Default address was None so setting to current instances') address = self.vip_address serverkey = self.serverkey if serverkey is None: self.logit("serverkey wasn't set but the address was.") raise Exception("Invalid state.") if publickey is None or secretkey is None: self.logit('generating new public secret key pair') keyfile = tempfile.mktemp(".keys", "agent", self.volttron_home) keys = KeyStore(keyfile) keys.generate() publickey = keys.public secretkey = keys.secret entry = AuthEntry(capabilities=capabilities, comments="Added by test", credentials=keys.public) file = AuthFile(self.volttron_home + "/auth.json") file.add(entry) conn = Connection(address=address, peer=peer, publickey=publickey, secretkey=secretkey, serverkey=serverkey, volttron_home=self.volttron_home) return conn
def setup_control_connection(request, get_volttron_instances): """ Creates a single instance of VOLTTRON for testing purposes """ global wrapper, control_connection wrapper = get_volttron_instances(1) request.addfinalizer(wrapper.shutdown_platform) assert wrapper assert wrapper.is_running() if get_volttron_instances.param == 'encrypted': if wrapper.encrypt: wrapper.allow_all_connections() # Connect using keys ks = KeyStore() ks.generate() control_connection = build_connection(identity="foo", address=wrapper.vip_address, peer=CONTROL, serverkey=wrapper.serverkey, publickey=ks.public, secretkey=ks.secret) else: control_connection = Connection(address=wrapper.local_vip_address, peer=CONTROL, developer_mode=True) # Sleep a couple seconds to wait for things to startup gevent.sleep(2) return wrapper, control_connection
def build_connection(identity, peer='', address=get_address(), publickey=ks.public, secretkey=ks.secret, **kwargs): cn = Connection(address=address, identity=identity, peer=peer, publickey=publickey, secretkey=secretkey, **kwargs) return cn
def test_listagents(pa_instance): try: wrapper, agent_uuid = pa_instance os.environ['VOLTTRON_HOME'] = wrapper.volttron_home agent = Connection(wrapper.local_vip_address, VOLTTRON_CENTRAL_PLATFORM) params = dict(id='foo', method='list_agents') agent_list = agent.call( 'route_request', 'foo', 'list_agents', None) assert 1 <= len(agent_list) expected_keys = ['name', 'uuid', 'tag', 'priority', 'process_id', 'health', 'health.status', 'heatlh.context', 'health.last_updated', 'error_code', 'permissions', 'permissions.can_restart', 'permissions.can_remove', 'can_stop', 'can_start'] expected_key_set = set(expected_keys) none_key_set = set(['tag', 'priority', 'health.context', 'error_code']) not_none_key_set = expected_key_set.difference(none_key_set) for a in agent_list: assert contains_keys(expected_keys, a) assert values_not_none(not_none_key_set, a) finally: os.environ.pop('VOLTTRON_HOME')
def build_connection(self, peer=None, address=None, identity=None, publickey=None, secretkey=None, serverkey=None, **kwargs): if self.encrypt: self.allow_all_connections() if address is None: address = self.vip_address serverkey = self.serverkey if publickey is None or secretkey is None: self.logit('generating new public secret key pair') keyfile = tempfile.mktemp(".keys", "agent", self.volttron_home) keys = KeyStore(keyfile) keys.generate() publickey = keys.public secretkey = keys.secret if self.encrypt: conn = Connection(address=address, peer=peer, publickey=publickey, secretkey=secretkey, serverkey=serverkey, volttron_home=self.volttron_home) else: conn = Connection(address=self.local_vip_address, peer=peer, volttron_home=self.volttron_home, developer_mode=True) return conn
def build_connection(identity, peer='', address=None, publickey=None, secretkey=None, message_bus=None, **kwargs): address = address if address is not None else get_address() if publickey is None or secretkey is None: publickey, secretkey = get_server_keys(publickey, secretkey) cn = Connection(address=address, identity=identity, peer=peer, publickey=publickey, secretkey=secretkey, message_bus=message_bus, **kwargs) return cn
def build_connection(self): return Connection(self.remote_vip, peer='', serverkey=self.remote_serverkey, publickey=self.core.publickey, secretkey=self.core.secretkey)
def _vc_connection(self): """ Attempt to connect to volttron central management console. The attempts will be done in the following order. 1. if peer is vc register with it. 2. volttron-central-tcp and serverkey 2. volttron-central-http (looks up tcp and serverkey) 3. volttron-central-ipc :param sender: :param kwargs: :return: """ assert self._agent_started, "cannot be called before onstart signal" if self._volttron_central_connection: # if connected return the connection. if self._volttron_central_connection.is_connected(5): _log.debug('Returning connection') return self._volttron_central_connection _log.debug("Resetting connection as the peer wasn't responding.") # reset the connection so we can try it again below. self._volttron_central_connection.kill() self._volttron_central_connection = None # First check to see if there is a peer with a volttron.central # identity, if there is use it as the manager of the platform. peers = self.vip.peerlist().get(timeout=5) if VOLTTRON_CENTRAL in peers: _log.debug('VC is a local peer.') self._volttron_central_connection = Connection( self.core.address, VOLTTRON_CENTRAL) if self._volttron_central_connection.is_connected() and \ self._volttron_central_connection.is_peer_connected(): _log.debug("Connection has been established to local peer.") return self._volttron_central_connection # If we have an http address for volttron central, but haven't # looked up the address yet, then look up and set the address from # volttron central discovery. if self._volttron_central_http_address is not None and \ self._volttron_central_tcp_address is None and \ self._volttron_central_serverkey is None: _log.debug('Using discovery to lookup tcp connection') response = requests.get("{}/discovery/".format( self._volttron_central_http_address)) if response.ok: jsonresp = response.json() entry = AuthEntry(credentials="/.*/", capabilities=['manager'] #, #address=jsonresp['vip-address'] ) authfile = AuthFile(get_home() + "/auth.json") authfile.add(entry) self._volttron_central_tcp_address = jsonresp['vip-address'] self._volttron_central_serverkey = jsonresp['serverkey'] # First see if we are able to connect via tcp with the serverkey. if self._volttron_central_tcp_address is not None and \ self._volttron_central_serverkey is not None: _log.debug('Connecting to volttron central using tcp.') vc_conn = Connection(address=self._volttron_central_tcp_address, peer=VOLTTRON_CENTRAL, serverkey=self._volttron_central_serverkey, publickey=self.core.publickey, secretkey=self.core.secretkey) if not vc_conn.is_connected(5): raise ValueError("Unable to connect to remote platform") if not vc_conn.is_peer_connected(5): raise ValueError( "Peer: {} unavailable on remote platform.".format( VOLTTRON_CENTRAL)) #TODO Only add a single time for this address. if self._volttron_central_publickey: # Add the vcpublickey to the auth file. entry = AuthEntry(credentials=self._volttron_central_publickey, capabilities=['manager']) authfile = AuthFile() authfile.add(entry) self._volttron_central_connection = vc_conn return self._volttron_central_connection # Next see if we have a valid ipc address (Not Local though) if self._volttron_central_ipc_address is not None: self._volttron_central_connection = Connection( address=self._volttron_central_ipc_address, peer=VOLTTRON_CENTRAL) return self._volttron_central_connection
class VolttronCentralPlatform(Agent): __name__ = 'VolttronCentralPlatform' def __init__(self, config_path, **kwargs): super(VolttronCentralPlatform, self).__init__(**kwargs) self._local_instance_name = None self._local_instance_uuid = None self._local_serverkey = None self._local_bind_web_address = None self._external_addresses = None self._volttron_central_reconnect_interval = 5 self._volttron_central_http_address = None self._volttron_central_tcp_address = None self._volttron_central_ipc_address = None self._volttron_central_serverkey = None self._volttron_central_publickey = None self._volttron_central_connection = None self._control_connection = None settings_path = os.path.join(get_home(), "data/vcp.settings") self._settings = load_create_store(settings_path) config = utils.load_config(config_path) self.reconfigure(**config) # Flag set after the platform has a guaranteed connection to the router. self._agent_started = False self._is_registering = False self._is_registered = False self._was_unmanaged = False self._devices = {} self._device_topic_hashes = {} self._topic_replace_map = {} # Default publish interval to 20 seconds. self._stats_publish_interval = 20 self._stats_publisher = None if config.get('stats-publish-interval') is None: config['stats-publish-interval'] = self._stats_publish_interval if self._settings.get('instance-uuid'): config['instance-uuid'] = self._settings.get('instance-uuid') if config.get('volttron-central-reconnect-interval') is None: config['volttron-central-reconnect-interval'] = \ self._volttron_central_reconnect_interval self._topic_replace_list = config.get("topic_replace_list", []) self.reconfigure(**config) # This is scheduled after first call to the reconnect function self._scheduled_connection_event = None @RPC.export def reconfigure(self, **kwargs): instance_name = kwargs.get('instance-name') instance_uuid = kwargs.get('instance-uuid') vc_address = kwargs.get('volttron-central-address') vc_serverkey = kwargs.get('volttron-central-serverkey') new_publish_interval = kwargs.get('stats-publish-interval') reconnect_interval = kwargs.get('volttron-central-reconnect-interval') if instance_name: self._local_instance_name = instance_name if instance_uuid: self._local_instance_uuid = instance_uuid if reconnect_interval: self._volttron_central_reconnect_interval = reconnect_interval if vc_address: parsed = urlparse.urlparse(vc_address) if parsed.scheme in ('http', 'https'): self._volttron_central_http_address = vc_address elif parsed.scheme == 'tcp': self._volttron_central_tcp_address = vc_address elif parsed.scheme == 'ipc': self._volttron_central_ipc_address = vc_address else: raise ValueError('Invalid volttron central address.') if vc_serverkey: if self._volttron_central_tcp_address: self._volttron_central_serverkey = vc_serverkey else: raise ValueError('Invalid volttron central tcp address.') if new_publish_interval is not None: if int(self._stats_publish_interval) < 20: raise ValueError( "stats publishing must be greater than 20 seconds.") self._stats_publish_interval = new_publish_interval self._start_stats_publisher() def _periodic_attempt_registration(self): if not self._started: _log.error('NOT STARTED!') return if self._scheduled_connection_event is not None: # This won't hurt anything if we are canceling ourselves. self._scheduled_connection_event.cancel() try: vc = self._vc_connection() if vc is None: _log.debug("vc not connected") return if not self._is_registering and not self._is_registered and \ not self._was_unmanaged: _log.debug("Starting the registration process from vcp.") _log.debug("Instance is named: {}".format( self._local_instance_name)) _log.debug('vc.address is: {}'.format(vc.address)) if vc.address.startswith('ipc'): self._vc_connection().call( "register_instance", address=self.core.address, display_name=self._local_instance_name, serverkey=self._local_serverkey, vcpagentkey=self.core.publickey) else: self._vc_connection().call( "register_instance", address=self._external_addresses[0], display_name=self._local_instance_name, serverkey=self._local_serverkey, vcpagentkey=self.core.publickey) else: _log.debug( "is registering: {}, is_registered: {}, was unmanaged {}". format(self._is_registering, self._is_registered, self._was_unmanaged)) except Unreachable as e: _log.error("Couldn't connect to volttron.central. {}".format( self._volttron_central_tcp_address)) except ValueError as e: _log.error(e.message) except Exception as e: _log.error("{} found as {}".format(e, e.message)) except gevent.Timeout as e: _log.error("timout occured connecting to remote platform.") finally: _log.debug('Scheduling next periodic call') now = get_aware_utc_now() next_update_time = now + datetime.timedelta( seconds=self._volttron_central_reconnect_interval) self._scheduled_connection_event = self.core.schedule( next_update_time, self._periodic_attempt_registration) def _vc_connection(self): """ Attempt to connect to volttron central management console. The attempts will be done in the following order. 1. if peer is vc register with it. 2. volttron-central-tcp and serverkey 2. volttron-central-http (looks up tcp and serverkey) 3. volttron-central-ipc :param sender: :param kwargs: :return: """ assert self._agent_started, "cannot be called before onstart signal" if self._volttron_central_connection: # if connected return the connection. if self._volttron_central_connection.is_connected(5): _log.debug('Returning connection') return self._volttron_central_connection _log.debug("Resetting connection as the peer wasn't responding.") # reset the connection so we can try it again below. self._volttron_central_connection.kill() self._volttron_central_connection = None # First check to see if there is a peer with a volttron.central # identity, if there is use it as the manager of the platform. peers = self.vip.peerlist().get(timeout=5) if VOLTTRON_CENTRAL in peers: _log.debug('VC is a local peer.') self._volttron_central_connection = Connection( self.core.address, VOLTTRON_CENTRAL) if self._volttron_central_connection.is_connected() and \ self._volttron_central_connection.is_peer_connected(): _log.debug("Connection has been established to local peer.") return self._volttron_central_connection # If we have an http address for volttron central, but haven't # looked up the address yet, then look up and set the address from # volttron central discovery. if self._volttron_central_http_address is not None and \ self._volttron_central_tcp_address is None and \ self._volttron_central_serverkey is None: _log.debug('Using discovery to lookup tcp connection') response = requests.get("{}/discovery/".format( self._volttron_central_http_address)) if response.ok: jsonresp = response.json() entry = AuthEntry(credentials="/.*/", capabilities=['manager'] #, #address=jsonresp['vip-address'] ) authfile = AuthFile(get_home() + "/auth.json") authfile.add(entry) self._volttron_central_tcp_address = jsonresp['vip-address'] self._volttron_central_serverkey = jsonresp['serverkey'] # First see if we are able to connect via tcp with the serverkey. if self._volttron_central_tcp_address is not None and \ self._volttron_central_serverkey is not None: _log.debug('Connecting to volttron central using tcp.') vc_conn = Connection(address=self._volttron_central_tcp_address, peer=VOLTTRON_CENTRAL, serverkey=self._volttron_central_serverkey, publickey=self.core.publickey, secretkey=self.core.secretkey) if not vc_conn.is_connected(5): raise ValueError("Unable to connect to remote platform") if not vc_conn.is_peer_connected(5): raise ValueError( "Peer: {} unavailable on remote platform.".format( VOLTTRON_CENTRAL)) #TODO Only add a single time for this address. if self._volttron_central_publickey: # Add the vcpublickey to the auth file. entry = AuthEntry(credentials=self._volttron_central_publickey, capabilities=['manager']) authfile = AuthFile() authfile.add(entry) self._volttron_central_connection = vc_conn return self._volttron_central_connection # Next see if we have a valid ipc address (Not Local though) if self._volttron_central_ipc_address is not None: self._volttron_central_connection = Connection( address=self._volttron_central_ipc_address, peer=VOLTTRON_CENTRAL) return self._volttron_central_connection @Core.receiver('onstart') def _started(self, sender, **kwargs): # Created a link to the control agent on this platform. self._control_connection = Connection(address=self.core.address, peer='control') _log.debug('Querying router for addresses and serverkey.') q = Query(self.core) self._external_addresses = q.query('addresses').get(timeout=2) _log.debug('External addresses are: {}'.format( self._external_addresses)) self._local_serverkey = q.query('serverkey').get(timeout=2) _log.debug('serverkey is: {}'.format(self._local_serverkey)) vc_http_address = q.query('volttron-central-address').get(timeout=2) _log.debug('vc address is {}'.format(vc_http_address)) self._local_instance_name = q.query('instance-name').get(timeout=2) _log.debug('instance-name is {}'.format(self._local_instance_name)) if vc_http_address is not None: parsed = urlparse.urlparse(vc_http_address) if parsed.scheme in ('https', 'http'): self._volttron_central_http_address = vc_http_address elif parsed.scheme == 'ipc': self._volttron_central_ipc_address = vc_http_address elif parsed.scheme == 'tcp': self._volttron_central_tcp_address = vc_http_address vc_serverkey = q.query('volttron-central-serverkey').get( timeout=2) if vc_serverkey is None: raise ValueError( 'volttron-central-serverkey is not set with tcp address' ) self._volttron_central_serverkey = vc_serverkey else: raise ValueError('invalid scheme for volttron-central-address') self._agent_started = True _log.debug('Starting the period registration attempts.') # Start the process of attempting registration with a VOLTTRON central. self._periodic_attempt_registration() try: _log.debug('Starting stats publisher.') self._start_stats_publisher() except ValueError as e: _log.error(e) def _start_stats_publisher(self): if not self._agent_started: return if self._stats_publisher: self._stats_publisher.kill() # The stats publisher publishes both to the local bus and the vc # bus the platform specific topics. self._stats_publisher = self.core.periodic( self._stats_publish_interval, self._publish_stats) @RPC.export def get_health(self): _log.debug("Getting health: {}".format(self.vip.health.get_status())) return self.vip.health.get_status() @RPC.export def get_instance_uuid(self): return self._local_instance_uuid @RPC.export def get_manager_key(self): return self._volttron_central_publickey @RPC.export def manage(self, address, vcserverkey=None, vcpublickey=None): """ Allows the `VolttronCentralPlatform` to be managed. From the web perspective this should be after the user has specified that a user has blessed an agent to be able to be managed. When the user enters a discovery address in `VolttronCentral` it is implied that the user wants to manage a platform. :returns publickey of the `VolttronCentralPlatform` """ _log.info('Manage request from address: {} serverkey: {}'.format( address, vcserverkey)) self._was_unmanaged = False parsed = urlparse.urlparse(address) same_address = False if parsed.scheme == 'ipc': if self._volttron_central_ipc_address == address: same_address = True self._volttron_central_ipc_address = address elif parsed.scheme == 'tcp': _log.debug('Found tcp scheme adding AuthEntry') if self._volttron_central_tcp_address == address: same_address = True self._volttron_central_tcp_address = address self._volttron_central_serverkey = vcserverkey # Add the vcpublickey to the auth file. entry = AuthEntry( credentials=vcpublickey, capabilities=['manager']) # , address=parsedaddress.hostname) authfile = AuthFile() authfile.add(entry) else: raise AttributeError('Invalid scheme in address') if self._vc_connection() is not None: self._is_registered = True self._is_registering = False _log.debug("Returning publickey from manage function.") return self.core.publickey raise NotManagedError( "Could not connect to specified volttron central") @RPC.export def unmanage(self): self._is_registering = False self._is_registered = False self._was_unmanaged = True @RPC.export # @RPC.allow("manager") #TODO: uncomment allow decorator def list_agents(self): """ List the agents that are installed on the platform. Note this only lists the agents that are actually installed on the instance. :return: A list of agents. """ return self._get_agent_list() @RPC.export # @RPC.allow("can_manage") def start_agent(self, agent_uuid): self._control_connection.call("start_agent", agent_uuid) @RPC.export # @RPC.allow("can_manage") def stop_agent(self, agent_uuid): proc_result = self._control_connection.call("stop_agent", agent_uuid) @RPC.export # @RPC.allow("can_manage") def restart_agent(self, agent_uuid): self._control_connection.call("restart_agent", agent_uuid) gevent.sleep(0.2) return self.agent_status(agent_uuid) @RPC.export def agent_status(self, agent_uuid): return self._control_connection.call("agent_status", agent_uuid) @RPC.export def status_agents(self): return self._control_connection.call('status_agents') @RPC.export def get_device(self, topic): _log.debug('Get device for topic: {}'.format(topic)) return self._devices.get(topic) @PubSub.subscribe('pubsub', 'devices') def _on_device_message(self, peer, sender, bus, topic, headers, message): # only deal with agents that have not been forwarded. if headers.get('X-Forwarded', None): return # only listen to the ending all message. if not re.match('.*/all$', topic): return topicsplit = topic.split('/') # For devices we use everything between devices/../all as a unique # key for determining the last time it was seen. key = '/'.join(topicsplit[1:-1]) anon_topic = self._topic_replace_map.get(key) publish_time_utc = format_timestamp(get_aware_utc_now()) if not anon_topic: anon_topic = key for sr in self._topic_replace_list: _log.debug('anon replacing {}->{}'.format( sr['from'], sr['to'])) anon_topic = anon_topic.replace(sr['from'], sr['to']) _log.debug('anon after replacing {}'.format(anon_topic)) _log.debug('Anon topic is: {}'.format(anon_topic)) self._topic_replace_map[key] = anon_topic _log.debug('Only anon topics are being listed.') hashable = anon_topic + str(message[0].keys()) _log.debug('Hashable is: {}'.format(hashable)) md5 = hashlib.md5(hashable) # self._md5hasher.update(hashable) hashed = md5.hexdigest() self._device_topic_hashes[hashed] = anon_topic self._devices[anon_topic] = { 'points': message[0].keys(), 'last_published_utc': publish_time_utc, 'md5hash': hashed } vc = self._vc_connection() if vc is not None: message = dict(md5hash=hashed, last_publish_utc=publish_time_utc) if self._local_instance_uuid is not None: vcp_topic = PLATFORM_VCP_DEVICES( platform_uuid=self._local_instance_uuid, topic=anon_topic) vc.publish(vcp_topic.format(), message=message) else: local_topic = PLATFORM( subtopic="devices/{}".format(anon_topic)) self.vip.pubsub.publish("pubsub", local_topic, message=message) _log.debug('Devices: {} Hashes: {} Platform: {}'.format( len(self._devices), self._device_topic_hashes, self._local_instance_name)) @RPC.export def get_devices(self): cp = deepcopy(self._devices) foundbad = False for k, v in cp.items(): dt = parse_timestamp_string(v['last_published_utc']) dtnow = get_aware_utc_now() if dt + datetime.timedelta(minutes=5) < dtnow: v['health'] = Status.build( BAD_STATUS, 'Too long between publishes for {}'.format(k)).as_dict() foundbad = True else: v['health'] = Status.build(GOOD_STATUS).as_dict() if len(cp): if foundbad: self.vip.health.set_status( BAD_STATUS, 'At least one device has not published in 5 minutes') else: self.vip.health.set_status(GOOD_STATUS, 'All devices publishing normally.') return cp @RPC.export def route_request(self, id, method, params): _log.debug('platform agent routing request: {}, {}'.format(id, method)) method_map = { 'list_agents': self.list_agents, 'get_devices': self.get_devices, } # First handle the elements that are going to this platform if method in method_map: result = method_map[method]() elif method == 'set_setting': result = self.set_setting(**params) elif method == 'get_setting': result = self.get_setting(**params) elif method == 'get_devices': result = self.get_devices() elif method == 'status_agents': _log.debug('Doing status agents') result = { 'result': [{ 'name': a[1], 'uuid': a[0], 'process_id': a[2][0], 'return_code': a[2][1] } for a in self._control_connection.call(method)] } elif method in ('agent_status', 'start_agent', 'stop_agent', 'remove_agent', 'restart_agent'): _log.debug('We are trying to exectute method {}'.format(method)) _log.debug('Params are: {}'.format(params)) if isinstance(params, list) and len(params) != 1 or \ isinstance(params, dict) and 'uuid' not in params.keys(): result = jsonrpc.json_error(ident=id, code=INVALID_PARAMS) else: if isinstance(params, list): uuid = params[0] elif isinstance(params, str): uuid = params else: uuid = params['uuid'] _log.debug('calling control with method: {} uuid: {}'.format( method, uuid)) status = self._control_connection.call(method, uuid) if method == 'stop_agent' or status == None: # Note we recurse here to get the agent status. result = self.route_request(id, 'agent_status', uuid) else: result = { 'process_id': status[0], 'return_code': status[1] } elif method in ('install'): if not 'files' in params: result = jsonrpc.json_error(ident=id, code=INVALID_PARAMS) else: result = self._install_agents(params['files']) else: fields = method.split('.') if fields[0] == 'historian': if 'platform.historian' in self.vip.peerlist().get(timeout=30): agent_method = fields[1] result = self.vip.rpc.call('platform.historian', agent_method, **params).get(timeout=45) else: result = jsonrpc.json_error(id, INVALID_PARAMS, 'historian unavailable') else: agent_uuid = fields[2] agent_method = '.'.join(fields[3:]) _log.debug("Calling method {} on agent {}".format( agent_method, agent_uuid)) _log.debug("Params is: {}".format(params)) # find the identity of the agent so we can call it by name. identity = self._control_connection.call( 'agent_vip_identity', agent_uuid) if params: if isinstance(params, list): result = self.vip.rpc.call(identity, agent_method, *params).get(timeout=30) else: result = self.vip.rpc.call(identity, agent_method, **params).get(timeout=30) else: result = self.vip.rpc.call(identity, agent_method).get(timeout=30) if isinstance(result, dict): if 'result' in result: return result['result'] elif 'code' in result: return result['code'] elif result is None: return return result def _install_agents(self, agent_files): tmpdir = tempfile.mkdtemp() results = [] for f in agent_files: try: if 'local' in f.keys(): path = f['file_name'] else: path = os.path.join(tmpdir, f['file_name']) with open(path, 'wb') as fout: fout.write( base64.decodestring(f['file'].split('base64,')[1])) _log.debug('Calling control install agent.') uuid = self.vip.rpc.call('control', 'install_agent_local', path).get() except Exception as e: results.append({'error': str(e)}) _log.error("EXCEPTION: " + str(e)) else: results.append({'uuid': uuid}) shutil.rmtree(tmpdir, ignore_errors=True) return results @RPC.export def list_agent_methods(self, method, params, id, agent_uuid): return jsonrpc.json_error(ident=id, code=INTERNAL_ERROR, message='Not implemented') def _publish_stats(self): """ Publish the platform statistics to the local bus as well as to the connected volttron central. """ vc_topic = None local_topic = LOGGER(subtopic="platform/status/cpu") _log.debug('Publishing platform cpu stats') if self._local_instance_uuid is not None: vc_topic = LOGGER(subtopic="platforms/{}/status/cpu".format( self._local_instance_uuid)) _log.debug('Stats will be published to: {}'.format( vc_topic.format())) else: _log.debug('Platform uuid is not valid') points = {} for k, v in psutil.cpu_times_percent().__dict__.items(): points['times_percent/' + k] = {'Readings': v, 'Units': 'double'} points['percent'] = { 'Readings': psutil.cpu_percent(), 'Units': 'double' } try: vc = self._vc_connection() if vc is not None and vc.is_connected() and vc_topic is not None: vc.publish(vc_topic.format(), message=points) except Exception as e: _log.info("status not written to volttron central.") self.vip.pubsub.publish(peer='pubsub', topic=local_topic.format(), message=points) @Core.receiver('onstop') def _stoping(self, sender, **kwargs): if self._volttron_central_connection is not None: self._volttron_central_connection.kill() self._volttron_central_connection = None if self._control_connection is not None: self._control_connection.kill() self._control_connection = None self._is_registered = False self._is_registering = False def _get_agent_list(self): """ Retrieve a list of agents on the platform. Each entry in the list :return: list: A list of agent data. """ agents = self._control_connection.call("list_agents") status_running = self.status_agents() uuid_to_status = {} # proc_info has a list of [startproc, endprox] for a in agents: pinfo = None is_running = False for uuid, name, proc_info in status_running: if a['uuid'] == uuid: is_running = proc_info[0] > 0 and proc_info[1] == None pinfo = proc_info break uuid_to_status[a['uuid']] = { 'is_running': is_running, 'process_id': None, 'error_code': None, 'permissions': { 'can_stop': is_running, 'can_start': not is_running, 'can_restart': True, 'can_remove': True } } if pinfo: uuid_to_status[a['uuid']]['process_id'] = proc_info[0] uuid_to_status[a['uuid']]['error_code'] = proc_info[1] if 'volttroncentral' in a['name'] or \ 'vcplatform' in a['name']: uuid_to_status[a['uuid']]['permissions']['can_stop'] = False uuid_to_status[a['uuid']]['permissions']['can_remove'] = False # The default agent is stopped health looks like this. uuid_to_status[a['uuid']]['health'] = { 'status': 'UNKNOWN', 'context': None, 'last_updated': None } if is_running: identity = self.vip.rpc.call('control', 'agent_vip_identity', a['uuid']).get(timeout=30) try: status = self.vip.rpc.call( identity, 'health.get_status').get(timeout=5) uuid_to_status[a['uuid']]['health'] = status except gevent.Timeout: _log.error("Couldn't get health from {} uuid: {}".format( identity, a['uuid'])) except Unreachable: _log.error( "Couldn't reach agent identity {} uuid: {}".format( identity, a['uuid'])) for a in agents: if a['uuid'] in uuid_to_status.keys(): _log.debug('UPDATING STATUS OF: {}'.format(a['uuid'])) a.update(uuid_to_status[a['uuid']]) return agents