def set_setting(self, session_user, params): """ Sets or removes a setting from the config store. If the value is None then the item will be removed from the store. If there is an error in saving the value then a jsonrpc.json_error object is returned. :param session_user: Unused :param params: Dictionary that must contain 'key' and 'value' keys. :return: A 'SUCCESS' string or a jsonrpc.json_error object. """ if 'key' not in params or not params['key']: return jsonrpc.json_error(params['message_id'], INVALID_PARAMS, 'Invalid parameter key not set') if 'value' not in params: return jsonrpc.json_error(params['message_id'], INVALID_PARAMS, 'Invalid parameter key not set') config_key = "settings/{}".format(params['key']) value = params['value'] if value is None: try: self.vip.config.delete(config_key) except KeyError: pass else: # We handle empt string here because the config store doesn't allow # empty strings to be set as a config store. I wasn't able to # trap the ValueError that is raised on the server side. if value == "": return jsonrpc.json_error(params['message_id'], INVALID_PARAMS, 'Invalid value set (empty string?)') self.vip.config.set(config_key, value) return 'SUCCESS'
def processMessage(self, message): _log.debug('processResponse()') result = 'FAILED' try: rpcdata = jsonrpc.JsonRpcData.parse(message) _log.info('rpc method: {}'.format(rpcdata.method)) if rpcdata.method == "rpc_setPlugThPP": args = {'plugID': rpcdata.params['plugID'], 'newThreshold': rpcdata.params['newThreshold'] } result = self.setThresholdPP(**args) elif rpcdata.method == "rpc_ping": result = True else: return jsonrpc.json_error('NA', METHOD_NOT_FOUND, 'Invalid method {}'.format(rpcdata.method)) return jsonrpc.json_result(rpcdata.id, result) except KeyError as ke: print(ke) return jsonrpc.json_error('NA', INVALID_PARAMS, 'Invalid params {}'.format(rpcdata.params)) except AssertionError: print('AssertionError') return jsonrpc.json_error('NA', INVALID_REQUEST, 'Invalid rpc data {}'.format(data)) except Exception as e: print(e) return jsonrpc.json_error('NA', UNHANDLED_EXCEPTION, e)
def rpc_from_net(self, header, message): _log.debug('rpc_from_net()') result = False try: rpcdata = jsonrpc.JsonRpcData.parse(message) _log.info('rpc method: {}'.format(rpcdata.method) + \ '; rpc params: {}'.format(rpcdata.params)) if rpcdata.method == "rpc_registerDsBridge": args = { 'discovery_address': rpcdata.params['discovery_address'], 'deviceId': rpcdata.params['deviceId'] } result = self._registerDsBridge(**args) elif rpcdata.method == "rpc_unregisterDsBridge": args = { 'discovery_address': rpcdata.params['discovery_address'], 'deviceId': rpcdata.params['deviceId'] } result = self._unregisterDsBridge(**args) elif rpcdata.method == "rpc_postEnergyDemand": args = { 'discovery_address': rpcdata.params['discovery_address'], 'deviceId': rpcdata.params['deviceId'], 'newEnergyDemand': rpcdata.params['newEnergyDemand'] } #post the new energy demand from ds to the local bus result = self._postEnergyDemand(**args) elif rpcdata.method == "rpc_postPricePoint": args = { 'discovery_address': rpcdata.params['discovery_address'], 'deviceId': rpcdata.params['deviceId'], 'newPricePoint': rpcdata.params['newPricePoint'] } #post the new new price point from us to the local-us-bus result = self._postPricePoint(**args) else: return jsonrpc.json_error(rpcdata.id, METHOD_NOT_FOUND, \ 'Invalid method {}'.format(rpcdata.method)) return jsonrpc.json_result(rpcdata.id, result) except KeyError as ke: print(ke) return jsonrpc.json_error( 'NA', INVALID_PARAMS, 'Invalid params {}'.format(rpcdata.params)) except Exception as e: print(e) return jsonrpc.json_error('NA', UNHANDLED_EXCEPTION, e)
def _handle_bacnet_scan(self, session_user, params): platform_uuid = params.pop('platform_uuid') id = params.pop('message_id') _log.debug('Handling bacnet_scan platform: {}'.format(platform_uuid)) if not self._platforms.is_registered(platform_uuid): return jsonrpc.json_error(id, UNAVAILABLE_PLATFORM, "Couldn't connect to platform {}".format( platform_uuid )) scan_length = params.pop('scan_length', 5) try: scan_length = float(scan_length) params['scan_length'] = scan_length platform = self._platforms.get_platform(platform_uuid) iam_topic = "{}/iam".format(session_user['token']) ws_socket_topic = "/vc/ws/{}".format(iam_topic) self.vip.web.register_websocket(ws_socket_topic, self.open_authenticate_ws_endpoint, self._ws_closed, self._ws_received) def start_scan(): # We want the datatype (iam) to be second in the response so # we need to reposition the iam and the session id to the topic # that is passed to the rpc function on vcp iam_session_topic = "iam/{}".format(session_user['token']) platform.call("start_bacnet_scan", iam_session_topic, **params) def close_socket(): _log.debug('Closing bacnet scan for {}'.format( platform_uuid)) gevent.spawn_later(2, self.vip.web.unregister_websocket, iam_session_topic) gevent.spawn_later(scan_length, close_socket) # By starting the scan a second later we allow the websocket # client to subscribe to the newly available endpoint. gevent.spawn_later(2, start_scan) except ValueError: return jsonrpc.json_error(id, UNAVAILABLE_PLATFORM, "Couldn't connect to platform {}".format( platform_uuid )) except KeyError: return jsonrpc.json_error(id, UNAUTHORIZED, "Invalid user session token")
def _handle_bacnet_scan(self, session_user, params): platform_uuid = params.pop('platform_uuid') id = params.pop('message_id') _log.debug('Handling bacnet_scan platform: {}'.format(platform_uuid)) if not self._platforms.is_registered(platform_uuid): return jsonrpc.json_error(id, UNAVAILABLE_PLATFORM, "Couldn't connect to platform {}".format( platform_uuid )) scan_length = params.pop('scan_length', 5) try: scan_length = float(scan_length) params['scan_length'] = scan_length platform = self._platforms.get_platform(platform_uuid) iam_topic = "{}/iam".format(session_user['token']) ws_socket_topic = "/vc/ws/{}".format(iam_topic) self.vip.web.register_websocket(ws_socket_topic, self.open_authenticate_ws_endpoint, self._ws_closed, self._ws_received) def start_scan(): # We want the datatype (iam) to be second in the response so # we need to reposition the iam and the session id to the topic # that is passed to the rpc function on vcp iam_session_topic = "iam/{}".format(session_user['token']) platform.call("start_bacnet_scan", iam_session_topic, **params) def close_socket(): _log.debug('Closing bacnet scan for {}'.format( platform_uuid)) gevent.spawn_later(2, self.vip.web.unregister_websocket, iam_session_topic) gevent.spawn_later(scan_length, close_socket) # By starting the scan a second later we allow the websocket # client to subscribe to the newly available endpoint. gevent.spawn_later(2, start_scan) except ValueError: return jsonrpc.json_error(id, UNAVAILABLE_PLATFORM, "Couldn't connect to platform {}".format( platform_uuid )) except KeyError: return jsonrpc.json_error(id, UNAUTHORIZED, "Invalid user session token")
def rpc_from_net(self, header, message): result = False try: rpcdata = jsonrpc.JsonRpcData.parse(message) ''' _log.debug('rpc_from_net()...' + \ ', rpc method: {}'.format(rpcdata.method) +\ ', rpc params: {}'.format(rpcdata.params)) ''' if rpcdata.method == "rpc_registerDsBridge": args = {'discovery_address': rpcdata.params['discovery_address'], 'deviceId':rpcdata.params['deviceId'] } result = self._registerDsBridge(**args) elif rpcdata.method == "rpc_unregisterDsBridge": args = {'discovery_address': rpcdata.params['discovery_address'], 'deviceId':rpcdata.params['deviceId'] } result = self._unregisterDsBridge(**args) elif rpcdata.method == "rpc_postEnergyDemand": args = {'discovery_address': rpcdata.params['discovery_address'], 'deviceId':rpcdata.params['deviceId'], 'newEnergyDemand': rpcdata.params['newEnergyDemand'] } #post the new energy demand from ds to the local bus result = self._postEnergyDemand(**args) elif rpcdata.method == "rpc_postPricePoint": args = {'discovery_address': rpcdata.params['discovery_address'], 'deviceId':rpcdata.params['deviceId'], 'newPricePoint': rpcdata.params['newPricePoint'] } #post the new new price point from us to the local-us-bus result = self._postPricePoint(**args) elif rpcdata.method == "rpc_ping": result = True else: return jsonrpc.json_error(rpcdata.id, METHOD_NOT_FOUND, \ 'Invalid method {}'.format(rpcdata.method)) return jsonrpc.json_result(rpcdata.id, result) except KeyError as ke: print(ke) return jsonrpc.json_error('NA', INVALID_PARAMS, 'Invalid params {}'.format(rpcdata.params)) except Exception as e: print(e) return jsonrpc.json_error('NA', UNHANDLED_EXCEPTION, e)
def _processMessage(self, message): #_log.debug('processResponse()') result = False try: rpcdata = jsonrpc.JsonRpcData.parse(message) _log.info('rpc method: {}'.format(rpcdata.method)) _log.info('rpc params: {}'.format(rpcdata.params)) if rpcdata.method == "rpc_setShDeviceState": args = {'deviceId': rpcdata.params['deviceId'], 'state': rpcdata.params['newState'], 'schdExist': SCHEDULE_NOT_AVLB } result = self.setShDeviceState(**args) elif rpcdata.method == "rpc_setShDeviceLevel": args = {'deviceId': rpcdata.params['deviceId'], 'level': rpcdata.params['newLevel'], 'schdExist': SCHEDULE_NOT_AVLB } result = self.setShDeviceLevel(**args) elif rpcdata.method == "rpc_setShDeviceThPP": args = {'deviceId': rpcdata.params['deviceId'], 'thPP': rpcdata.params['newThPP'] } result = self.setShDeviceThPP(**args) elif rpcdata.method == "rpc_ping": result = True else: return jsonrpc.json_error('NA', METHOD_NOT_FOUND, 'Invalid method {}'.format(rpcdata.method)) result = True return jsonrpc.json_result(rpcdata.id, result) except KeyError as ke: print(ke) return jsonrpc.json_error('NA', INVALID_PARAMS, 'Invalid params {}'.format(rpcdata.params)) except AssertionError: print('AssertionError') return jsonrpc.json_error('NA', INVALID_REQUEST, 'Invalid rpc data {}'.format(data)) except Exception as e: print(e) return jsonrpc.json_error('NA', UNHANDLED_EXCEPTION, e)
def _disable_setup_mode(self, session_user, params): id = params.pop('message_id') if 'admin' not in session_user['groups']: _log.debug('Returning json_error disable_setup_mode') return jsonrpc.json_error( id, UNAUTHORIZED, "Admin access is required to disable setup mode") self.vip.rpc.call(AUTH, "auth_file.remove_by_credentials", "/.*/") return "SUCCESS"
def _disable_setup_mode(self, session_user, params): id = params.pop('message_id') if 'admin' not in session_user['groups']: _log.debug('Returning json_error disable_setup_mode') return jsonrpc.json_error( id, UNAUTHORIZED, "Admin access is required to disable setup mode") auth_file = AuthFile() auth_file.remove_by_credentials("/.*/") return "SUCCESS"
def post(self): _log.debug('handling request') request_body = self.request.body # result will be an RpcParser object when completed self.rpcrequest = RpcRequest(request_body) if self.rpcrequest.has_error(): self._response_complete( jsonrpc.json_error( self.rpcrequest.id, self.rpcrequest.error, get_standard_error_message(self.rpcrequest.error))) else: self._route(self.rpcrequest)
def _get_jsonrpc_response(self, id, result_or_error): """ Wrap the response in either a json-rpc error or result. :param id: :param result_or_error: :return: """ if 'error' in result_or_error: error = result_or_error['error'] _log.debug("RPC RESPONSE ERROR: {}".format(error)) return jsonrpc.json_error(id, error['code'], error['message']) return jsonrpc.json_result(id, result_or_error)
def _get_jsonrpc_response(self, id, result_or_error): """ Wrap the response in either a json-rpc error or result. :param id: :param result_or_error: :return: """ if 'error' in result_or_error: error = result_or_error['error'] _log.debug("RPC RESPONSE ERROR: {}".format(error)) return jsonrpc.json_error(id, error['code'], error['message']) return jsonrpc.json_result(id, result_or_error)
def set_setting(self, session_user, params): """ Sets or removes a setting from the config store. If the value is None then the item will be removed from the store. If there is an error in saving the value then a jsonrpc.json_error object is returned. :param session_user: Unused :param params: Dictionary that must contain 'key' and 'value' keys. :return: A 'SUCCESS' string or a jsonrpc.json_error object. """ if 'key' not in params or not params['key']: return jsonrpc.json_error(params['message_id'], INVALID_PARAMS, 'Invalid parameter key not set') if 'value' not in params: return jsonrpc.json_error(params['message_id'], INVALID_PARAMS, 'Invalid parameter key not set') config_key = "settings/{}".format(params['key']) value = params['value'] if value is None: try: self.vip.config.delete(config_key) except KeyError: pass else: # We handle empt string here because the config store doesn't allow # empty strings to be set as a config store. I wasn't able to # trap the ValueError that is raised on the server side. if value == "": return jsonrpc.json_error(params['message_id'], INVALID_PARAMS, 'Invalid value set (empty string?)') self.vip.config.set(config_key, value) return 'SUCCESS'
def route_to_agent_method(self, id, agent_method, params): try: self._log.debug('route_to_agent_method {} {}'.format( id, agent_method)) resp = self.call('route_to_agent_method', id, agent_method, params) if isinstance(resp, dict): if 'result' not in resp and 'error' not in resp: resp = jsonrpc.json_result(id, resp) else: resp = jsonrpc.json_result(id, resp) return resp except RemoteError as e: return jsonrpc.json_error(id, INTERNAL_ERROR, "Internal Error: {}".format(str(e)))
def route_to_agent_method(self, id, agent_method, params): try: self._log.debug('route_to_agent_method {} {}'.format(id, agent_method)) resp = self.call('route_to_agent_method', id, agent_method, params) if isinstance(resp, dict): if 'result' not in resp and 'error' not in resp: resp = jsonrpc.json_result(id, resp) else: resp = jsonrpc.json_result(id, resp) return resp except RemoteError as e: return jsonrpc.json_error(id, INTERNAL_ERROR, "Internal Error: {}".format(str(e)))
def post(self): _log.debug("handling request") self.tmp.seek(0) self.rpcrequest = RpcRequest(self.tmp.read()) self.tmp.close() # request_body = self.request.body # result will be an RpcParser object when completed # self.rpcrequest = RpcRequest(request_body) if self.rpcrequest.has_error(): self._response_complete( jsonrpc.json_error( self.rpcrequest.id, self.rpcrequest.error, get_standard_error_message(self.rpcrequest.error) ) ) else: self._route(self.rpcrequest)
def get_setting(self, session_user, params): """ Retrieve a value from the passed setting key. The params object must contain a "key" to return from the settings store. :param session_user: Unused :param params: Dictionary that must contain a 'key' key. :return: The value or a jsonrpc error object. """ config_key = "settings/{}".format(params['key']) try: value = self.vip.config.get(config_key) except KeyError: return jsonrpc.json_error(params['message_id'], INVALID_PARAMS, 'Invalid key specified') else: return value
def _enable_setup_mode(self, session_user, params): id = params.pop('message_id') if 'admin' not in session_user['groups']: _log.debug('Returning json_error enable_setup_mode') return jsonrpc.json_error( id, UNAUTHORIZED, "Admin access is required to enable setup mode") auth_file = AuthFile() entries = auth_file.find_by_credentials(".*") if len(entries) > 0: return "SUCCESS" entry = AuthEntry(credentials="/.*/", comments="Un-Authenticated connections allowed here", user_id="unknown") auth_file.add(entry) return "SUCCESS"
def _enable_setup_mode(self, session_user, params): id = params.pop('message_id') if 'admin' not in session_user['groups']: _log.debug('Returning json_error enable_setup_mode') return jsonrpc.json_error( id, UNAUTHORIZED, "Admin access is required to enable setup mode") auth_file = AuthFile() entries = auth_file.find_by_credentials(".*") if len(entries) > 0: return "SUCCESS" entry = AuthEntry(credentials="/.*/", comments="Un-Authenticated connections allowed here", user_id="unknown") auth_file.add(entry) return "SUCCESS"
def get_setting(self, session_user, params): """ Retrieve a value from the passed setting key. The params object must contain a "key" to return from the settings store. :param session_user: Unused :param params: Dictionary that must contain a 'key' key. :return: The value or a jsonrpc error object. """ config_key = "settings/{}".format(params['key']) try: value = self.vip.config.get(config_key) except KeyError: return jsonrpc.json_error(params['message_id'], INVALID_PARAMS, 'Invalid key specified') else: return value
def _enable_setup_mode(self, session_user, params): id = params.pop('message_id') if 'admin' not in session_user['groups']: _log.debug('Returning json_error enable_setup_mode') return jsonrpc.json_error( id, UNAUTHORIZED, "Admin access is required to enable setup mode") entries = self.vip.rpc.call(AUTH, "auth_file.find_by_credentials", ".*") if len(entries) > 0: return "SUCCESS" entry = { "credentials": "/.*/", "comments": "Un-Authenticated connections allowed here", "user_id": "unknown" } self.vip.rpc.call(AUTH, "auth_file.add", entry) return "SUCCESS"
def store_agent_config(self, session_user, params): required = ('agent_identity', 'config_name', 'raw_contents') message_id = params.pop('message_id') errors = [] for r in required: if r not in params: errors.append('Missing {}'.format(r)) config_type = params.get('config_type', None) if config_type: if config_type not in ('raw', 'json', 'csv'): errors.append('Invalid config_type parameter') if errors: return jsonrpc.json_error(message_id, INVALID_PARAMS, "\n".join(errors)) try: self._log.debug("Calling store_agent_config on external platform.") self.call("store_agent_config", **params) except Exception as e: self._log.error(str(e)) return jsonrpc.json_error(message_id, INTERNAL_ERROR, str(e)) config_name = params.get("config_name") agent_identity = params.get("agent_identity") if config_name.startswith("devices"): # Since we start with devices, we assume that we are attempting # to save a master driver config file. rawdict = jsonapi.loads(params['raw_contents']) # if this is not a bacnet device_type then we cannot do anything # more than save and retrieve it from the store. driver_type = rawdict.get('driver_type', None) if driver_type is None or driver_type not in ('bacnet', 'modbus'): return jsonrpc.json_result(message_id, "SUCCESS") # Registry config starts with config:// registry_config = rawdict['registry_config'][len('config://'):] try: self._log.debug("Retrieving registry_config for new device.") point_config = self.call("get_agent_config", agent_identity, registry_config, raw=False) except Exception as e: self._log.error(str(e)) return jsonrpc.json_error(message_id, INTERNAL_ERROR, "Couldn't retrieve registry_config " "from connection.") else: new_device = dict( device_address=rawdict['driver_config']['device_address'], device_id=rawdict['driver_config']['device_id'], points=[], path=config_name, health=Status.build(UNKNOWN_STATUS, context="Unpublished").as_dict() ) points = [p['Volttron Point Name'] for p in point_config] new_device['points'] = points self._vc.send_management_message("NEW_DEVICE", new_device) status = Status.build(UNKNOWN_STATUS, context="Not published since update") device_config_name = params.get('config_name') device_no_prefix = device_config_name[len('devices/'):] the_device = self._current_devices.get(device_no_prefix, {}) if not the_device: self._current_devices[device_no_prefix] = dict( last_publish_utc=None, health=status.as_dict(), points=points ) else: self._current_devices[device_no_prefix]['points'] = points return jsonrpc.json_result(message_id, "SUCCESS")
def _route_request(self, session_user, id, method, params): """ Handle the methods volttron central can or pass off to platforms. :param session_user: The authenticated user's session info. :param id: JSON-RPC id field. :param method: :param params: :return: """ _log.debug( 'inside _route_request {}, {}, {}'.format(id, method, params)) def err(message, code=METHOD_NOT_FOUND): return {'error': {'code': code, 'message': message}} self.send_management_message(method) method_split = method.split('.') # The last part of the jsonrpc method is the actual method to be called. method_check = method_split[-1] # These functions will be sent to a platform.agent on either this # instance or another. All of these functions have the same interface # and can be collected into a dictionary rather than an if tree. platform_methods = dict( # bacnet related start_bacnet_scan=self._handle_bacnet_scan, publish_bacnet_props=self._handle_bacnet_props, # config store related store_agent_config="store_agent_config", get_agent_config="get_agent_config", list_agent_configs="get_agent_config_list", # management related list_agents="get_agent_list", get_devices="get_devices", status_agents="status_agents" ) # These methods are specifically to be handled by the platform not any # agents on the platform that is why we have the length requirement. # # The jsonrpc method looks like the following # # platform.uuid.<dynamic entry>.method_on_vcp if method_check in platform_methods: platform_uuid = None if isinstance(params, dict): platform_uuid = params.pop('platform_uuid', None) if platform_uuid is None: if method_split[0] == 'platforms' and method_split[1] == 'uuid': platform_uuid = method_split[2] if not platform_uuid: return err("Invalid platform_uuid specified as parameter" .format(platform_uuid), INVALID_PARAMS) if not self._platforms.is_registered(platform_uuid): return err("Unknown or unavailable platform {} specified as " "parameter".format(platform_uuid), UNAVAILABLE_PLATFORM) try: _log.debug('Calling {} on platform {}'.format( method_check, platform_uuid )) class_method = platform_methods[method_check] platform = self._platforms.get_platform(platform_uuid) # Determine whether the method to call is on the current class # or on the platform object. if isinstance(class_method, basestring): method_ref = getattr(platform, class_method) else: method_ref = class_method # Put the platform_uuid in the params so it can be used # inside the method params['platform_uuid'] = platform_uuid except AttributeError or KeyError: return jsonrpc.json_error(id, INTERNAL_ERROR, "Attempted calling function " "{} was unavailable".format( class_method )) except ValueError: return jsonrpc.json_error(id, UNAVAILABLE_PLATFORM, "Couldn't connect to platform " "{}".format(platform_uuid)) else: # pass the id through the message_id parameter. if not params: params = dict(message_id=id) else: params['message_id'] = id # Methods will all have the signature # method(session, params) # return method_ref(session_user, params) vc_methods = dict( register_management_endpoint=self._handle_management_endpoint, list_platforms=self._platforms.get_platform_list, list_performance=self._platforms.get_performance_list, # Settings set_setting=self.set_setting, get_setting=self.get_setting, get_setting_keys=self.get_setting_keys, # Setup mode enable_setup_mode=self._enable_setup_mode, disable_setup_mode=self._disable_setup_mode ) if method in vc_methods: if not params: params = dict(message_id=id) else: params['message_id'] = id response = vc_methods[method](session_user, params) _log.debug("Response is {}".format(response)) return response # vc_methods[method](session_user, params) if method == 'register_instance': if isinstance(params, list): return self._register_instance(*params) else: return self._register_instance(**params) elif method == 'unregister_platform': return self.unregister_platform(params['instance_uuid']) elif 'historian' in method: has_platform_historian = PLATFORM_HISTORIAN in \ self.vip.peerlist().get(timeout=30) if not has_platform_historian: return err( 'The VOLTTRON Central platform historian is unavailable.', UNAVAILABLE_AGENT) _log.debug('Trapping platform.historian to vc.') _log.debug('has_platform_historian: {}'.format( has_platform_historian)) if 'historian.query' in method: return self.vip.rpc.call( PLATFORM_HISTORIAN, 'query', **params).get(timeout=30) elif 'historian.get_topic_list' in method: return self.vip.rpc.call( PLATFORM_HISTORIAN, 'get_topic_list').get(timeout=30) # This isn't known as a proper method on vc or a platform. if len(method_split) < 3: return err('Unknown method {}'.format(method)) if method_split[0] != 'platforms' or method_split[1] != 'uuid': return err('Invalid format for instance must start with ' 'platforms.uuid') instance_uuid = method_split[2] _log.debug('Instance uuid is: {}'.format(instance_uuid)) if not self._platforms.is_registered(instance_uuid): return err('Unknown platform {}'.format(instance_uuid)) platform_method = '.'.join(method_split[3:]) _log.debug("Platform method is: {}".format(platform_method)) platform = self._platforms.get_platform(instance_uuid) if not platform: return jsonrpc.json_error(id, UNAVAILABLE_PLATFORM, "cannot connect to platform." ) if platform_method.startswith('install'): if 'admin' not in session_user['groups']: return jsonrpc.json_error( id, UNAUTHORIZED, "Admin access is required to install agents") return platform.route_to_agent_method(id, platform_method, params)
def list_agent_methods(self, method, params, id, agent_uuid): return jsonrpc.json_error(ident=id, code=INTERNAL_ERROR, message='Not implemented')
def jsonrpc(self, env, data): """ The main entry point for ^jsonrpc data This method will only accept rpcdata. The first time this method is called, per session, it must be using get_authorization. That will return a session token that must be included in every subsequent request. The session is tied to the ip address of the caller. :param object env: Environment dictionary for the request. :param object data: The JSON-RPC 2.0 method to call. :return object: An JSON-RPC 2.0 response. """ if env['REQUEST_METHOD'].upper() != 'POST': return jsonrpc.json_error('NA', INVALID_REQUEST, 'Invalid request method, only POST allowed' ) try: rpcdata = self._to_jsonrpc_obj(data) _log.info('rpc method: {}'.format(rpcdata.method)) if rpcdata.method == 'get_authorization': args = {'username': rpcdata.params['username'], 'password': rpcdata.params['password'], 'ip': env['REMOTE_ADDR']} sess = self._authenticated_sessions.authenticate(**args) if not sess: _log.info('Invalid username/password for {}'.format( rpcdata.params['username'])) return jsonrpc.json_error( rpcdata.id, UNAUTHORIZED, "Invalid username/password specified.") _log.info('Session created for {}'.format( rpcdata.params['username'])) self.vip.web.register_websocket( "/vc/ws/{}/management".format(sess), self.open_authenticate_ws_endpoint, self._ws_closed, self._received_data) _log.info('Session created for {}'.format( rpcdata.params['username'])) gevent.sleep(1) return jsonrpc.json_result(rpcdata.id, sess) token = rpcdata.authorization ip = env['REMOTE_ADDR'] _log.debug('REMOTE_ADDR: {}'.format(ip)) session_user = self._authenticated_sessions.check_session(token, ip) _log.debug('SESSION_USER IS: {}'.format(session_user)) if not session_user: _log.debug("Session Check Failed for Token: {}".format(token)) return jsonrpc.json_error(rpcdata.id, UNAUTHORIZED, "Invalid authentication token") _log.debug('RPC METHOD IS: {}'.format(rpcdata.method)) # Route any other method that isn't result_or_error = self._route_request(session_user, rpcdata.id, rpcdata.method, rpcdata.params) except AssertionError: return jsonrpc.json_error( 'NA', INVALID_REQUEST, 'Invalid rpc data {}'.format(data)) except Unreachable: return jsonrpc.json_error( rpcdata.id, UNAVAILABLE_PLATFORM, "Couldn't reach platform with method {} params: {}".format( rpcdata.method, rpcdata.params)) except Exception as e: return jsonrpc.json_error( 'NA', UNHANDLED_EXCEPTION, e ) return self._get_jsonrpc_response(rpcdata.id, result_or_error)
def list_agent_methods(self, method, params, id, agent_uuid): return jsonrpc.json_error(ident=id, code=INTERNAL_ERROR, message='Not implemented')
def _route_request(self, session_user, id, method, params): '''Route request to either a registered platform or handle here.''' _log.debug('inside _route_request {}, {}, {}'.format( id, method, params)) def err(message, code=METHOD_NOT_FOUND): return {'error': {'code': code, 'message': message}} if method == 'register_instance': if isinstance(params, list): return self._register_instance(*params) else: return self._register_instance(**params) elif method == 'list_deivces': return self._handle_list_devices() elif method == 'list_performance': return self._handle_list_performance() elif method == 'list_platforms': return self._handle_list_platforms() elif method == 'unregister_platform': return self.unregister_platform(params['platform_uuid']) elif method == 'get_setting': if 'key' not in params or not params['key']: return err('Invalid parameter key not set', INVALID_PARAMS) value = self._setting_store.get(params['key'], None) if value is None: return err('Invalid key specified', INVALID_PARAMS) return value elif method == 'get_setting_keys': return self._setting_store.keys() elif method == 'set_setting': if 'key' not in params or not params['key']: return err('Invalid parameter key not set', INVALID_PARAMS) _log.debug('VALUE: {}'.format(params)) if 'value' not in params: return err('Invalid parameter value not set', INVALID_PARAMS) # if passing None value then remove the value from the keystore # don't raise an error if the key isn't present in the store. if params['value'] is None: if params['key'] in self._setting_store: del self._setting_store[params['key']] else: self._setting_store[params['key']] = params['value'] self._setting_store.sync() return 'SUCCESS' elif 'historian' in method: has_platform_historian = PLATFORM_HISTORIAN in \ self.vip.peerlist().get(timeout=30) if not has_platform_historian: return err( 'The VOLTTRON Central platform historian is unavailable.', UNAVAILABLE_AGENT) _log.debug('Trapping platform.historian to vc.') _log.debug( 'has_platform_historian: {}'.format(has_platform_historian)) if 'historian.query' in method: return self.vip.rpc.call(PLATFORM_HISTORIAN, 'query', **params).get(timeout=30) elif 'historian.get_topic_list' in method: return self.vip.rpc.call(PLATFORM_HISTORIAN, 'get_topic_list').get(timeout=30) fields = method.split('.') if len(fields) < 3: return err('Unknown method {}'.format(method)) platform_uuid = fields[2] platform = self._registry.get_platform(platform_uuid) if not platform: return err('Unknown platform {}'.format(platform_uuid)) platform_method = '.'.join(fields[3:]) _log.debug(platform_uuid) # Get a connection object associated with the platform uuid. cn = self._pa_agents.get(platform_uuid) if not cn: return jsonrpc.json_error(id, UNAVAILABLE_PLATFORM, "Cannot connect to platform.") _log.debug('Routing to {}'.format(VOLTTRON_CENTRAL_PLATFORM)) if platform_method == 'install': if 'admin' not in session_user['groups']: return jsonrpc.json_error( id, UNAUTHORIZED, "Admin access is required to install agents") if platform_method == 'list_agents': _log.debug('Callling list_agents') agents = self._registry.get_agent_list(platform_uuid) if agents is None: _log.warn('No agents found for platform_uuid {}'.format( platform_uuid)) agents = [] for a in agents: if 'admin' not in session_user['groups']: a['permissions'] = { 'can_stop': False, 'can_start': False, 'can_restart': False, 'can_remove': False } else: _log.debug('Permissionse for {} are {}'.format( a['name'], a['permissions'])) return agents else: try: return cn.agent.vip.rpc.call(VOLTTRON_CENTRAL_PLATFORM, 'route_request', id, platform_method, params).get(timeout=30) except (Unreachable, gevent.Timeout) as e: del self._pa_agents[platform_uuid] return err("Can't route to platform", UNAVAILABLE_PLATFORM)
def route_request(self, id, method, params): _log.debug( 'platform agent routing request: {}, {}'.format(id, method)) # First handle the elements that are going to this platform if method == 'list_agents': result = self.list_agents() 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.vip.rpc.call('control', method).get()]} elif method in ('agent_status', 'start_agent', 'stop_agent', 'remove_agent'): _log.debug('We are trying to exectute method {}'.format(method)) 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'] status = self.vip.rpc.call('control', method, uuid).get() 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)) result = self.vip.rpc.call(agent_uuid, agent_method, **params).get() if isinstance(result, dict): if 'result' in result: return result['result'] elif 'code' in result: return result['code'] return result
def _route_request(self, session_user, id, method, params): """ Handle the methods volttron central can or pass off to platforms. :param session_user: The authenticated user's session info. :param id: JSON-RPC id field. :param method: :param params: :return: """ _log.debug( 'inside _route_request {}, {}, {}'.format(id, method, params)) def err(message, code=METHOD_NOT_FOUND): return {'error': {'code': code, 'message': message}} self.send_management_message(method) method_split = method.split('.') # The last part of the jsonrpc method is the actual method to be called. method_check = method_split[-1] # These functions will be sent to a platform.agent on either this # instance or another. All of these functions have the same interface # and can be collected into a dictionary rather than an if tree. platform_methods = dict( # bacnet related start_bacnet_scan=self._handle_bacnet_scan, publish_bacnet_props=self._handle_bacnet_props, # config store related store_agent_config="store_agent_config", get_agent_config="get_agent_config", list_agent_configs="get_agent_config_list", # management related list_agents="get_agent_list", get_devices="get_devices", status_agents="status_agents" ) # These methods are specifically to be handled by the platform not any # agents on the platform that is why we have the length requirement. # # The jsonrpc method looks like the following # # platform.uuid.<dynamic entry>.method_on_vcp if method_check in platform_methods: platform_uuid = None if isinstance(params, dict): platform_uuid = params.pop('platform_uuid', None) if platform_uuid is None: if method_split[0] == 'platforms' and method_split[1] == 'uuid': platform_uuid = method_split[2] if not platform_uuid: return err("Invalid platform_uuid specified as parameter" .format(platform_uuid), INVALID_PARAMS) if not self._platforms.is_registered(platform_uuid): return err("Unknown or unavailable platform {} specified as " "parameter".format(platform_uuid), UNAVAILABLE_PLATFORM) try: _log.debug('Calling {} on platform {}'.format( method_check, platform_uuid )) class_method = platform_methods[method_check] platform = self._platforms.get_platform(platform_uuid) # Determine whether the method to call is on the current class # or on the platform object. if isinstance(class_method, basestring): method_ref = getattr(platform, class_method) else: method_ref = class_method # Put the platform_uuid in the params so it can be used # inside the method params['platform_uuid'] = platform_uuid except AttributeError or KeyError: return jsonrpc.json_error(id, INTERNAL_ERROR, "Attempted calling function " "{} was unavailable".format( class_method )) except ValueError: return jsonrpc.json_error(id, UNAVAILABLE_PLATFORM, "Couldn't connect to platform " "{}".format(platform_uuid)) else: # pass the id through the message_id parameter. if not params: params = dict(message_id=id) else: params['message_id'] = id # Methods will all have the signature # method(session, params) # return method_ref(session_user, params) vc_methods = dict( register_management_endpoint=self._handle_management_endpoint, list_platforms=self._platforms.get_platform_list, list_performance=self._platforms.get_performance_list, # Settings set_setting=self.set_setting, get_setting=self.get_setting, get_setting_keys=self.get_setting_keys, # Setup mode enable_setup_mode=self._enable_setup_mode, disable_setup_mode=self._disable_setup_mode ) if method in vc_methods: if not params: params = dict(message_id=id) else: params['message_id'] = id response = vc_methods[method](session_user, params) _log.debug("Response is {}".format(response)) return response # vc_methods[method](session_user, params) if method == 'register_instance': if isinstance(params, list): return self._register_instance(*params) else: return self._register_instance(**params) elif method == 'unregister_platform': return self.unregister_platform(params['instance_uuid']) elif 'historian' in method: has_platform_historian = PLATFORM_HISTORIAN in \ self.vip.peerlist().get(timeout=30) if not has_platform_historian: return err( 'The VOLTTRON Central platform historian is unavailable.', UNAVAILABLE_AGENT) _log.debug('Trapping platform.historian to vc.') _log.debug('has_platform_historian: {}'.format( has_platform_historian)) if 'historian.query' in method: return self.vip.rpc.call( PLATFORM_HISTORIAN, 'query', **params).get(timeout=30) elif 'historian.get_topic_list' in method: return self.vip.rpc.call( PLATFORM_HISTORIAN, 'get_topic_list').get(timeout=30) # This isn't known as a proper method on vc or a platform. if len(method_split) < 3: return err('Unknown method {}'.format(method)) if method_split[0] != 'platforms' or method_split[1] != 'uuid': return err('Invalid format for instance must start with ' 'platforms.uuid') instance_uuid = method_split[2] _log.debug('Instance uuid is: {}'.format(instance_uuid)) if not self._platforms.is_registered(instance_uuid): return err('Unknown platform {}'.format(instance_uuid)) platform_method = '.'.join(method_split[3:]) _log.debug("Platform method is: {}".format(platform_method)) platform = self._platforms.get_platform(instance_uuid) if not platform: return jsonrpc.json_error(id, UNAVAILABLE_PLATFORM, "cannot connect to platform." ) if platform_method.startswith('install'): if 'admin' not in session_user['groups']: return jsonrpc.json_error( id, UNAUTHORIZED, "Admin access is required to install agents") return platform.route_to_agent_method(id, platform_method, params)
def jsonrpc(self, env, data): """ The main entry point for ^jsonrpc data This method will only accept rpcdata. The first time this method is called, per session, it must be using get_authorization. That will return a session token that must be included in every subsequent request. The session is tied to the ip address of the caller. :param object env: Environment dictionary for the request. :param object data: The JSON-RPC 2.0 method to call. :return object: An JSON-RPC 2.0 response. """ if env['REQUEST_METHOD'].upper() != 'POST': return jsonrpc.json_error('NA', INVALID_REQUEST, 'Invalid request method') try: rpcdata = self._to_jsonrpc_obj(data) _log.info('rpc method: {}'.format(rpcdata.method)) if rpcdata.method == 'get_authorization': args = {'username': rpcdata.params['username'], 'password': rpcdata.params['password'], 'ip': env['REMOTE_ADDR']} sess = self._sessions.authenticate(**args) if not sess: _log.info('Invalid username/password for {}'.format( rpcdata.params['username'])) return jsonrpc.json_error( rpcdata.id, UNAUTHORIZED, "Invalid username/password specified.") _log.info('Session created for {}'.format( rpcdata.params['username'])) return jsonrpc.json_result(rpcdata.id, sess) token = rpcdata.authorization ip = env['REMOTE_ADDR'] _log.debug('REMOTE_ADDR: {}'.format(ip)) session_user = self._sessions.check_session(token, ip) _log.debug('SESSION_USER IS: {}'.format(session_user)) if not session_user: _log.debug("Session Check Failed for Token: {}".format(token)) return jsonrpc.json_error(rpcdata.id, UNAUTHORIZED, "Invalid authentication token") _log.debug('RPC METHOD IS: {}'.format(rpcdata.method)) # Route any other method that isn't result_or_error = self._route_request(session_user, rpcdata.id, rpcdata.method, rpcdata.params) except AssertionError: return jsonrpc.json_error( 'NA', INVALID_REQUEST, 'Invalid rpc data {}'.format(data)) except Exception as e: return jsonrpc.json_error( 'NA', UNHANDLED_EXCEPTION, e ) _log.debug("RETURNING: {}".format(self._get_jsonrpc_response( rpcdata.id, result_or_error))) return self._get_jsonrpc_response(rpcdata.id, result_or_error)
def _route_request(self, session_user, id, method, params): '''Route request to either a registered platform or handle here.''' _log.debug( 'inside _route_request {}, {}, {}'.format(id, method, params)) def err(message, code=METHOD_NOT_FOUND): return {'error': {'code': code, 'message': message}} if method == 'register_instance': if isinstance(params, list): return self._register_instance(*params) else: return self._register_instance(**params) elif method == 'list_deivces': return self._handle_list_devices() elif method == 'list_performance': return self._handle_list_performance() elif method == 'list_platforms': return self._handle_list_platforms() elif method == 'unregister_platform': return self.unregister_platform(params['platform_uuid']) elif method == 'get_setting': if 'key' not in params or not params['key']: return err('Invalid parameter key not set', INVALID_PARAMS) value = self._setting_store.get(params['key'], None) if value is None: return err('Invalid key specified', INVALID_PARAMS) return value elif method == 'get_setting_keys': return self._setting_store.keys() elif method == 'set_setting': if 'key' not in params or not params['key']: return err('Invalid parameter key not set', INVALID_PARAMS) _log.debug('VALUE: {}'.format(params)) if 'value' not in params: return err('Invalid parameter value not set', INVALID_PARAMS) # if passing None value then remove the value from the keystore # don't raise an error if the key isn't present in the store. if params['value'] is None: if params['key'] in self._setting_store: del self._setting_store[params['key']] else: self._setting_store[params['key']] = params['value'] self._setting_store.sync() return 'SUCCESS' elif 'historian' in method: has_platform_historian = PLATFORM_HISTORIAN in \ self.vip.peerlist().get(timeout=30) if not has_platform_historian: return err('Platform historian not found on volttorn central', UNAVAILABLE_AGENT) _log.debug('Trapping platform.historian to vc.') _log.debug('has_platform_historian: {}'.format( has_platform_historian)) if 'historian.query' in method: return self.vip.rpc.call( PLATFORM_HISTORIAN, 'query', **params).get(timeout=30) elif 'historian.get_topic_list' in method: return self.vip.rpc.call( PLATFORM_HISTORIAN, 'get_topic_list').get(timeout=30) fields = method.split('.') if len(fields) < 3: return err('Unknown method {}'.format(method)) platform_uuid = fields[2] platform = self._registry.get_platform(platform_uuid) if not platform: return err('Unknown platform {}'.format(platform_uuid)) platform_method = '.'.join(fields[3:]) _log.debug(platform_uuid) # Get a connection object associated with the platform uuid. cn = self._pa_agents.get(platform_uuid) if not cn: return jsonrpc.json_error(id, UNAVAILABLE_PLATFORM, "Cannot connect to platform." ) _log.debug('Routing to {}'.format(VOLTTRON_CENTRAL_PLATFORM)) if platform_method == 'install': if 'admin' not in session_user['groups']: return jsonrpc.json_error( id, UNAUTHORIZED, "Admin access is required to install agents") if platform_method == 'list_agents': _log.debug('Callling list_agents') agents = self._registry.get_agent_list(platform_uuid) if agents is None: _log.warn('No agents found for platform_uuid {}'.format( platform_uuid )) agents = [] for a in agents: if 'admin' not in session_user['groups']: a['permissions'] = { 'can_stop': False, 'can_start': False, 'can_restart': False, 'can_remove': False } else: _log.debug('Permissionse for {} are {}' .format(a['name'], a['permissions'])) return agents else: try: return cn.agent.vip.rpc.call( VOLTTRON_CENTRAL_PLATFORM, 'route_request', id, platform_method, params).get(timeout=30) except (Unreachable, gevent.Timeout) as e: del self._pa_agents[platform_uuid] return err("Can't route to platform", UNAVAILABLE_PLATFORM)
def store_agent_config(self, session_user, params): required = ('agent_identity', 'config_name', 'raw_contents') message_id = params.pop('message_id') errors = [] for r in required: if r not in params: errors.append('Missing {}'.format(r)) config_type = params.get('config_type', None) if config_type: if config_type not in ('raw', 'json', 'csv'): errors.append('Invalid config_type parameter') if errors: return jsonrpc.json_error(message_id, INVALID_PARAMS, "\n".join(errors)) try: self._log.debug("Calling store_agent_config on external platform.") self.call("store_agent_config", **params) except Exception as e: self._log.error(str(e)) return jsonrpc.json_error(message_id, INTERNAL_ERROR, str(e)) config_name = params.get("config_name") agent_identity = params.get("agent_identity") if config_name.startswith("devices"): # Since we start with devices, we assume that we are attempting # to save a master driver config file. rawdict = jsonapi.loads(params['raw_contents']) # if this is not a bacnet device_type then we cannot do anything # more than save and retrieve it from the store. driver_type = rawdict.get('driver_type', None) if driver_type is None or driver_type not in ('bacnet', 'modbus'): return jsonrpc.json_result(message_id, "SUCCESS") # Registry config starts with config:// registry_config = rawdict['registry_config'][len('config://'):] try: self._log.debug("Retrieving registry_config for new device.") point_config = self.call("get_agent_config", agent_identity, registry_config, raw=False) except Exception as e: self._log.error(str(e)) return jsonrpc.json_error( message_id, INTERNAL_ERROR, "Couldn't retrieve registry_config " "from connection.") else: new_device = dict( device_address=rawdict['driver_config']['device_address'], device_id=rawdict['driver_config']['device_id'], points=[], path=config_name, health=Status.build(UNKNOWN_STATUS, context="Unpublished").as_dict()) points = [p['Volttron Point Name'] for p in point_config] new_device['points'] = points self._vc.send_management_message("NEW_DEVICE", new_device) status = Status.build(UNKNOWN_STATUS, context="Not published since update") device_config_name = params.get('config_name') device_no_prefix = device_config_name[len('devices/'):] the_device = self._current_devices.get(device_no_prefix, {}) if not the_device: self._current_devices[device_no_prefix] = dict( last_publish_utc=None, health=status.as_dict(), points=points) else: self._current_devices[device_no_prefix]['points'] = points return jsonrpc.json_result(message_id, "SUCCESS")
def route_request(self, id, method, params): _log.debug('platform agent routing request: {}, {}'.format(id, method)) # First handle the elements that are going to this platform if method == 'list_agents': result = self.list_agents() 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.vip.rpc.call('control', method).get()] } elif method in ('agent_status', 'start_agent', 'stop_agent', 'remove_agent'): _log.debug('We are trying to exectute method {}'.format(method)) 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'] status = self.vip.rpc.call('control', method, uuid).get() 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)) result = self.vip.rpc.call(agent_uuid, agent_method, **params).get() if isinstance(result, dict): if 'result' in result: return result['result'] elif 'code' in result: return result['code'] return result
def route_request(self, id, method, params): _log.debug('platform agent routing request: {}, {}'.format(id, method)) _log.debug(params) 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.vip.rpc.call(CONTROL, method).get(timeout=5)] } elif method in ('agent_status', 'start_agent', 'stop_agent', 'remove_agent', 'restart_agent'): _log.debug('We are trying to exectute method {}'.format(method)) 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.vip.rpc.call(CONTROL, method, uuid).get(timeout=5) if method == 'stop_agent' or status is 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', ): _log.debug("Attempting install!") if 'files' not in params: result = jsonrpc.json_error( ident=id, code=INVALID_PARAMS, message="Invalid parameter missing 'files'") else: # TODD: This should be only a single file at a time for installs fileargs = params.get('files')[0] result = self._install_agent(fileargs) 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)) if agent_method in ('start_bacnet_scan', 'publish_bacnet_props'): identity = params.pop("proxy_identity") if agent_method == 'start_bacnet_scan': result = self.start_bacnet_scan(identity, **params) elif agent_method == 'publish_bacnet_props': result = self.publish_bacnet_props(identity, **params) else: # find the identity of the agent so we can call it by name. identity = self.vip.rpc.call(CONTROL, 'agent_vip_identity', agent_uuid).get(timeout=5) 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) # find the identity of the agent so we can call it by name. identity = self.vip.rpc.call(CONTROL, 'agent_vip_identity', agent_uuid).get(timeout=5) 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 jsonrpc(self, env, data): """ The main entry point for ^jsonrpc data This method will only accept rpcdata. The first time this method is called, per session, it must be using get_authorization. That will return a session token that must be included in every subsequent request. The session is tied to the ip address of the caller. :param object env: Environment dictionary for the request. :param object data: The JSON-RPC 2.0 method to call. :return object: An JSON-RPC 2.0 response. """ if env['REQUEST_METHOD'].upper() != 'POST': return jsonrpc.json_error( 'NA', INVALID_REQUEST, 'Invalid request method, only POST allowed') try: rpcdata = self._to_jsonrpc_obj(data) _log.info('rpc method: {}'.format(rpcdata.method)) if rpcdata.method == 'get_authorization': # Authentication url # This does not need to be local, however for now we are going to # make it so assuming only one level of authentication. auth_url = "{url_scheme}://{HTTP_HOST}/authenticate".format( url_scheme=env['wsgi.url_scheme'], HTTP_HOST=env['HTTP_HOST']) user = rpcdata.params['username'] args = { 'username': rpcdata.params['username'], 'password': rpcdata.params['password'], 'ip': env['REMOTE_ADDR'] } resp = requests.post(auth_url, json=args, verify=False) if resp.ok and resp.text: claims = self.vip.web.get_user_claims(resp.text) # Because the web-user.json has the groups under a key and the # groups is just passed into the session we need to make sure # we pass in the proper thing to the _add_sesion function. assert 'groups' in claims authentication_token = resp.text sess = authentication_token self._authenticated_sessions._add_session( user=user, groups=claims['groups'], token=authentication_token, ip=env['REMOTE_ADDR']) else: sess = self._authenticated_sessions.authenticate(**args) if not sess: _log.info('Invalid username/password for {}'.format( rpcdata.params['username'])) return jsonrpc.json_error( rpcdata.id, UNAUTHORIZED, "Invalid username/password specified.") _log.info('Session created for {}'.format( rpcdata.params['username'])) self.vip.web.register_websocket( "/vc/ws/{}/management".format(sess), self.open_authenticate_ws_endpoint, self._ws_closed, self._received_data) _log.info('Session created for {}'.format( rpcdata.params['username'])) gevent.sleep(1) return jsonrpc.json_result(rpcdata.id, sess) token = rpcdata.authorization ip = env['REMOTE_ADDR'] _log.debug('REMOTE_ADDR: {}'.format(ip)) session_user = self._authenticated_sessions.check_session( token, ip) _log.debug('SESSION_USER IS: {}'.format(session_user)) if not session_user: _log.debug("Session Check Failed for Token: {}".format(token)) return jsonrpc.json_error(rpcdata.id, UNAUTHORIZED, "Invalid authentication token") _log.debug('RPC METHOD IS: {}'.format(rpcdata.method)) # Route any other method that isn't result_or_error = self._route_request(session_user, rpcdata.id, rpcdata.method, rpcdata.params) except AssertionError: return jsonrpc.json_error('NA', INVALID_REQUEST, 'Invalid rpc data {}'.format(data)) except Unreachable: return jsonrpc.json_error( rpcdata.id, UNAVAILABLE_PLATFORM, "Couldn't reach platform with method {} params: {}".format( rpcdata.method, rpcdata.params)) except Exception as e: return jsonrpc.json_error('NA', UNHANDLED_EXCEPTION, e) return self._get_jsonrpc_response(rpcdata.id, result_or_error)