def get_agent_log(self, kwargs): """Return logfile""" node_ids = [ str(node.id) for node in self.nodes ] exp_params = [('agentId', is_in_list(node_ids)), ('filename', is_string, None)] try: agent_id, filename = check_arguments(exp_params, kwargs) # Get the node that has the specified agent_id node = [ node for node in self.nodes if node.id == agent_id ][0] # If a filename was specified... if filename: # Get the logs for the node's role logs = self.get_role_logs(node.role) # Check that the filename is valid for that role filenames = map(lambda log: log['filename'], logs) exp_params = [('filename', is_in_list(filenames))] check_arguments(exp_params, dict(filename=filename)) # Replace the filename with its corresponding path in the agent filename = [ log['path'] for log in logs if log['filename'] == filename ][0] res = self.fetch_agent_log(node.ip, self.AGENT_PORT, filename) return HttpJsonResponse(res) except Exception as ex: return HttpErrorResponse("%s" % ex)
def list_volumes(self, kwargs): exp_params = [('service_id', is_in_list(self.httpsserver.instances.keys()), 0)] try: service_id = check_arguments(exp_params, kwargs) except Exception as ex: return HttpErrorResponse("%s" % ex) try: self.check_state([self.S_RUNNING, self.S_ADAPTING]) except: return HttpJsonResponse({ 'volumes': [] }) vols = copy.copy(self.volumes) rep_vols = [] for vol_name in vols: vol = vols[vol_name] vol['service_id'], vol['service_name'] = self.get_service_id_by_vm_id(vol['vm_id']) if service_id != 0: if vol['service_id'] == service_id: rep_vols.append(vol) else: rep_vols.append(vol) return HttpJsonResponse({ 'volumes': rep_vols })
def execute_script(self, kwargs): valid_commands = [ 'run', 'interrupt', 'cleanup' ] exp_params = [('command', is_in_list(valid_commands)), ('parameters', is_string, '')] try: command, parameters = check_arguments(exp_params, kwargs) self.check_state([self.S_RUNNING]) except Exception as ex: return HttpErrorResponse("%s" % ex) self.logger.info("Received request for executing the '%s' command " "with parameters '%s'" % (command, parameters)) # if command is 'interrupt' and no scripts are running, return an error if command == 'interrupt' and not self._are_scripts_running(): self.logger.info("No scripts are currently running inside agents") return HttpErrorResponse(self.NO_SCRIPTS_ARE_RUNNING_MSG) # for the other commands, if scripts are already running, return an error elif command != 'interrupt' and self._are_scripts_running(): self.logger.info("Scripts are already running inside at least " "one agent") return HttpErrorResponse(self.SCRIPTS_ARE_RUNNING_MSG) Thread(target=self._do_execute_script, args=[command, self.nodes, parameters]).start() return HttpJsonResponse({ 'state': self.state_get() })
def updateTomcatCode(self, kwargs): valid_filetypes = [ 'zip', 'tar', 'git' ] exp_params = [('filetype', is_in_list(valid_filetypes)), ('codeVersionId', is_string), ('file', is_uploaded_file, None), ('revision', is_string, '')] try: filetype, codeVersionId, file, revision = check_arguments(exp_params, kwargs) if filetype != 'git' and not file: raise Exception("The '%s' filetype requires an uploaded file" % filetype) elif filetype == 'git' and not revision: raise Exception("The 'git' filetype requires the 'revision' parameter") except Exception as ex: return HttpErrorResponse("%s" % ex) if filetype == 'zip': source = zipfile.ZipFile(file.file, 'r') elif filetype == 'tar': source = tarfile.open(fileobj=file.file) elif filetype == 'git': source = git.DEFAULT_CODE_REPO target_dir = join(self.VAR_CACHE, 'tomcat_instance', 'webapps', codeVersionId) if exists(target_dir): rmtree(target_dir) if filetype == 'git': subdir = str(self.SERVICE_ID) self.logger.debug("git_enable_revision('%s', '%s', '%s', '%s')" % (target_dir, source, revision, subdir)) git.git_enable_revision(target_dir, source, revision, subdir) else: source.extractall(target_dir) return HttpJsonResponse()
def update_php_configuration(self, kwargs): config = self._configuration_get() exp_params = [('codeVersionId', is_in_list(config.codeVersions), None), ('phpconf', is_dict, {})] try: codeVersionId, phpconf = check_arguments(exp_params, kwargs) self.check_state([self.S_INIT, self.S_STOPPED, self.S_RUNNING]) except Exception as ex: return HttpErrorResponse("%s" % ex) if codeVersionId is None and not phpconf: return HttpErrorResponse(ManagerException(ManagerException.E_ARGS_MISSING, 'at least one of "codeVersionId" or "phpconf"').message) if phpconf: for key in phpconf.keys(): if key not in config.backend_config.php_conf.defaults: return HttpErrorResponse(ManagerException(ManagerException.E_ARGS_UNEXPECTED, 'phpconf attribute "%s"' % (str(key))).message) if not re.match(config.backend_config.php_conf.format[key], phpconf[key]): return HttpErrorResponse(ManagerException(ManagerException.E_ARGS_INVALID).message) state = self.state_get() if state == self.S_INIT or state == self.S_STOPPED: if codeVersionId: config.currentCodeVersion = codeVersionId for key in phpconf: config.backend_config.php_conf.conf[key] = phpconf[key] self._configuration_set(config) elif state == self.S_RUNNING: self.state_set(self.S_ADAPTING, msg='Updating configuration') Thread(target=self.do_update_configuration, args=[config, codeVersionId, phpconf]).start() return HttpJsonResponse()
def get_node_info(self, kwargs): """Return information about the node identified by the given kwargs['serviceNodeId']""" node_ids = [ str(node.id) for node in self.nodes ] exp_params = [('serviceNodeId', is_in_list(node_ids))] try: serviceNodeId = check_arguments(exp_params, kwargs) except Exception as ex: return HttpErrorResponse("%s" % ex) serviceNode = None for node in self.nodes: if serviceNodeId == node.id: serviceNode = node break return HttpJsonResponse({ 'serviceNode': { 'id': serviceNode.id, 'ip': serviceNode.ip, 'vmid': serviceNode.vmid, 'cloud': serviceNode.cloud_name, 'is_master': self.__is_master(serviceNode), 'role': serviceNode.role, 'logs': self.get_role_logs(serviceNode.role) } })
def delete_code_version(self, kwargs): config = self._configuration_get() exp_params = [('codeVersionId', is_in_list(config.codeVersions))] try: codeVersionId = check_arguments(exp_params, kwargs) except Exception as ex: return HttpErrorResponse("%s" % ex) if codeVersionId == config.currentCodeVersion: ex = ManagerException(ManagerException.E_ARGS_INVALID, detail='Cannot remove the active code version') return HttpErrorResponse("%s" % ex) if not config.codeVersions[codeVersionId].type == 'git': filename = os.path.abspath(os.path.join(self.code_repo, codeVersionId)) if not filename.startswith(self.code_repo + '/') or not os.path.exists(filename): ex = ManagerException(ManagerException.E_ARGS_INVALID, detail='Invalid codeVersionId') return HttpErrorResponse("%s" % ex) os.remove(filename) config.codeVersions.pop(codeVersionId) self._configuration_set(config) return HttpJsonResponse()
def remove_service(self, kwargs): exp_params = [('service_id', is_in_list(self.httpsserver.instances.keys()))] try: service_id = check_arguments(exp_params, kwargs) except Exception as ex: return HttpErrorResponse("%s" % ex) self.state_set(self.S_ADAPTING) Thread(target=self._do_stop_service, args=[service_id, True]).start() return HttpJsonResponse()
def stop_service(self, kwargs): exp_params = [('service_id', is_in_list(self.httpsserver.instances.keys()))] try: service_id = check_arguments(exp_params, kwargs) except Exception as ex: return HttpErrorResponse("%s" % ex) service_manager = self.httpsserver.instances[service_id] service_manager.state_set(self.S_EPILOGUE) Thread(target=self._do_stop_service, args=[service_id, False]).start() return HttpJsonResponse({ 'state': service_manager.state_get() })
def get_startup_script(self, kwargs): """Return contents of the currently defined startup script, if any""" exp_params = [('sid', is_in_list(self.httpsserver.instances.keys()))] try: service_id = check_arguments(exp_params, kwargs) except Exception as ex: return HttpErrorResponse("%s" % ex) basedir = self.config_parser.get('manager', 'CONPAAS_HOME') fullpath = os.path.join(basedir, str(service_id), 'startup.sh') try: return HttpJsonResponse(file_get_contents(fullpath)) except IOError: return HttpErrorResponse('No startup script')
def update_code(self, kwargs): valid_filetypes = [ 'zip', 'tar', 'git' ] exp_params = [('filetype', is_in_list(valid_filetypes)), ('codeVersionId', is_string), ('file', is_uploaded_file, None), ('revision', is_string, '')] try: filetype, codeVersionId, file, revision = check_arguments(exp_params, kwargs) if filetype != 'git' and not file: raise Exception("The '%s' filetype requires an uploaded file" % filetype) elif filetype == 'git' and not revision: raise Exception("The 'git' filetype requires the 'revision' parameter") except Exception as ex: return HttpErrorResponse("%s" % ex) self.logger.info("Updating code to version '%s'" % codeVersionId) if filetype == 'zip': source = zipfile.ZipFile(file.file, 'r') elif filetype == 'tar': source = tarfile.open(fileobj=file.file) elif filetype == 'git': source = git.DEFAULT_CODE_REPO # kill all scripts that may still be running if self.processes: self._kill_all_processes() self.processes = {} target_dir = self.CODE_DIR if exists(target_dir): rmtree(target_dir) if filetype == 'git': subdir = str(self.SERVICE_ID) self.logger.debug("git_enable_revision('%s', '%s', '%s', '%s')" % (target_dir, source, revision, subdir)) git.git_enable_revision(target_dir, source, revision, subdir) else: source.extractall(target_dir) self.logger.info("Code updated, executing the 'init' command") # every time a new code tarball is activated, execute the init.sh script self._execute_script('init') return HttpJsonResponse()
def add_service(self, kwargs): """Expose methods relative to a specific service manager""" exp_params = [('service_type', is_in_list(manager_services.keys()))] try: service_type = check_arguments(exp_params, kwargs) except Exception as ex: return HttpErrorResponse("%s" % ex) self.kwargs.update(kwargs) services = manager_services try: module = __import__(services[service_type]['module'], globals(), locals(), ['*']) except ImportError: self.state_set(self.S_ERROR) raise Exception('Could not import module containing service class "%(module)s"' % services[service_type]) # Get the appropriate class for this service service_class = services[service_type]['class'] try: instance_class = getattr(module, service_class) except AttributeError: self.state_set(self.S_ERROR) raise Exception('Could not get service class %s from module %s' % (service_class, module)) self.state_set(self.S_ADAPTING) #probably lock it self.service_id = self.service_id + 1 service_config_parser = self.__copy_config_parser(self.config_parser) self._add_manager_configuration(service_config_parser, str(self.service_id), service_type) # self._run_manager_start_script(service_config_parser, service_type) #Create an instance of the service class service_insance = instance_class(service_config_parser, **self.kwargs) self.httpsserver.instances[self.service_id] = service_insance service_manager_exposed_functions = service_insance.get_exposed_methods() for http_method in service_manager_exposed_functions: for func_name in service_manager_exposed_functions[http_method]: self.httpsserver._register_method(http_method, self.service_id, func_name, service_manager_exposed_functions[http_method][func_name]) self.state_set(self.S_RUNNING) return HttpJsonResponse({ 'service_id': self.service_id })
def remove_nodes(self, kwargs): exp_params = [('service_id', is_in_list(self.httpsserver.instances.keys())), ('nodes', is_dict)] try: service_id, node_roles = check_arguments(exp_params, kwargs) service_manager = self.httpsserver.instances[service_id] service_manager.check_node_roles(node_roles) service_manager.check_state([self.S_RUNNING]) service_manager.check_remove_nodes(node_roles) except Exception as ex: return HttpErrorResponse("%s" % ex) service_manager.state_set(self.S_ADAPTING) Thread(target=self._do_remove_nodes, args=[service_id, node_roles]).start() return HttpJsonResponse({ 'state': service_manager.state_get() })
def start_service(self, kwargs): exp_params = [('service_id', is_in_list(self.httpsserver.instances.keys())), ('cloud', is_string)] try: service_id, cloud = check_arguments(exp_params, kwargs) self.check_credits() service_manager = self.httpsserver.instances[service_id] service_manager.check_state([self.S_INIT, self.S_STOPPED]) except Exception as ex: return HttpErrorResponse("%s" % ex) service_manager.logger.info('Manager starting up') service_manager.state_set(self.S_PROLOGUE) Thread(target=self._do_start_service, args=[service_id, cloud]).start() return HttpJsonResponse({ 'state': service_manager.state_get() })
def execute_script(self, kwargs): valid_commands = [ 'notify', 'run', 'interrupt', 'cleanup' ] exp_params = [('command', is_in_list(valid_commands)), ('parameters', is_string, ''), ('agents_info', is_string)] try: command, parameters, agents_info = check_arguments(exp_params, kwargs) agents_info = simplejson.loads(agents_info) except Exception as ex: return HttpErrorResponse("%s" % ex) if command == 'notify': self.logger.info("Executing the '%s' command" % command) else: self.logger.info("Executing the '%s' command with parameters '%s'" % (command, parameters)) target_dir = self.VAR_CACHE with open(join(target_dir, 'agents.json'), 'w') as outfile: simplejson.dump(agents_info, outfile) if command == 'interrupt': # if no script is running, do nothing if not self._are_scripts_running(): self.logger.info("No scripts are currently running") # if interrupt is already running, kill all processes elif self._get_script_status('interrupt') == 'RUNNING': self.logger.info("Script 'interrupt.sh' is already running") self._kill_all_processes() # execute the script and afterwards kill all processes else: Thread(target=self._do_interrupt, args=[parameters]).start() else: # if scripts are already running, do nothing if self._are_scripts_running(): self.logger.info("Scripts are already running") # execute the script else: self._execute_script(command, parameters) return HttpJsonResponse()
def update_java_configuration(self, kwargs): config = self._configuration_get() exp_params = [('codeVersionId', is_in_list(config.codeVersions))] try: codeVersionId = check_arguments(exp_params, kwargs) self.check_state([self.S_INIT, self.S_STOPPED, self.S_RUNNING]) except Exception as ex: return HttpErrorResponse("%s" % ex) state = self.state_get() if state == self.S_INIT or state == self.S_STOPPED: if codeVersionId: config.currentCodeVersion = codeVersionId self._configuration_set(config) elif state == self.S_RUNNING: self.state_set(self.S_ADAPTING, msg='Updating configuration') Thread(target=self.do_update_configuration, args=[config, codeVersionId]).start() return HttpJsonResponse()
def delete_volume(self, kwargs): exp_params = [('volumeName', is_in_list(self.volumes.keys()))] try: vol_name = check_arguments(exp_params, kwargs) self.check_volume_name(vol_name) volume = self.volumes[vol_name] node = [ node for node in self.nodes if node.id == volume['vm_id'] ][0] service_id, _ = self.get_service_id_by_vm_id(node.id) service_manager = self.httpsserver.instances[service_id] service_manager.check_state([self.S_RUNNING]) service_manager.check_delete_volume(node, volume) except Exception as ex: return HttpErrorResponse("%s" % ex) service_manager.state_set(self.S_ADAPTING) Thread(target=self._do_delete_volume, args=[volume, node, service_manager]).start() return HttpJsonResponse({ 'service_id': service_id })
def download_code_version(self, kwargs): config = self._configuration_get() exp_params = [('codeVersionId', is_in_list(config.codeVersions), config.currentCodeVersion)] try: codeVersionId = check_arguments(exp_params, kwargs) except Exception as ex: return HttpErrorResponse("%s" % ex) if config.codeVersions[codeVersionId].type == 'git': return HttpErrorResponse( 'ERROR: To download this code, please clone the git repository'); filename = os.path.abspath(os.path.join(self.code_repo, codeVersionId)) if not filename.startswith(self.code_repo + '/') or not os.path.exists(filename): ex = ManagerException(ManagerException.E_ARGS_INVALID, detail='Invalid codeVersionId') return HttpErrorResponse("%s" % ex) return HttpFileDownloadResponse(config.codeVersions[codeVersionId].filename, filename)
def create_volume(self, kwargs): node_ids = [ str(node.id) for node in self.nodes ] exp_params = [('volumeName', is_not_in_list(self.volumes.keys())), ('volumeSize', is_pos_int), ('agentId', is_in_list(node_ids))] try: vol_name, vol_size, agent_id = check_arguments(exp_params, kwargs) self.check_volume_name(vol_name) service_id, _ = self.get_service_id_by_vm_id(agent_id) service_manager = self.httpsserver.instances[service_id] service_manager.check_state([self.S_RUNNING]) service_manager.check_create_volume(vol_name, vol_size, agent_id) except Exception as ex: return HttpErrorResponse("%s" % ex) service_manager.state_set(self.S_ADAPTING) Thread(target=self._do_create_volume, args=[vol_name, vol_size, agent_id, service_manager]).start() return HttpJsonResponse({ 'service_id': service_id })
def enable_code(self, kwargs): config = self._configuration_get() exp_params = [('codeVersionId', is_in_list(config.codeVersions))] try: codeVersionId = check_arguments(exp_params, kwargs) self.check_state([self.S_INIT, self.S_STOPPED, self.S_RUNNING]) except Exception as ex: return HttpErrorResponse("%s" % ex) state = self.state_get() if state == self.S_INIT or state == self.S_STOPPED: config.currentCodeVersion = codeVersionId self._configuration_set(config) elif state == self.S_RUNNING: if self._are_scripts_running(): self.logger.info("Code activation is disabled when scripts are "\ "running") return HttpErrorResponse(self.SCRIPTS_ARE_RUNNING_MSG); self.state_set(self.S_ADAPTING, msg='Updating configuration') Thread(target=self._do_enable_code, args=[config, codeVersionId]).start() return HttpJsonResponse()
def get_node_info(self, kwargs): """ Gets info of a specific node. Parameters ---------- serviceNodeId : string identifier of node to query Returns a dict with keys: serviceNode : dict with keys: id : string node identifier ip : string node public IP address vmid : string unique identifier of the VM inside the cloud provider cloud :string name of cloud provider """ try: node_ids = self.config.serviceNodes.keys() + self.config.glb_service_nodes.keys() exp_params = [('serviceNodeId', is_in_list(node_ids))] serviceNodeId = check_arguments(exp_params, kwargs) except Exception as ex: return HttpErrorResponse("%s" % ex) serviceNode = self.config.getMySQLNode(serviceNodeId) return HttpJsonResponse({'serviceNode': {'id': serviceNode.id, 'ip': serviceNode.ip, 'vmid': serviceNode.vmid, 'cloud': serviceNode.cloud_name, 'isNode': serviceNode.isNode, 'isGlb_node': serviceNode.isGlb_node, 'role': serviceNode.role, 'logs': self.get_role_logs(serviceNode.role) } })
def on_autoscaling(self, kwargs): """ Enable auto-scaling for this PHP service. The auto-scaling will add or remove nodes to reach the expected response time in milliseconds while keeping a low credit usage. POST parameters --------------- cool_down : int time in minutes between adaptation points reponse_time : int service level objective (SLO) for the web and backend response time in milliseconds strategy : string one of "low", "medium_down", "medium_up", "medium", "high" """ self.logger.info('on_autoscaling entering') try: valid_strategy = [ 'low', 'medium_down', 'medium_up', 'medium', 'high' ] exp_params = [('cool_down', is_pos_nul_int), ('reponse_time', is_pos_nul_int), ('strategy', is_in_list(valid_strategy))] cool_down, reponse_time, strategy = check_arguments(exp_params, kwargs) if not self.scaler: raise Exception( 'Provisioning Manager has not been initialized: %s' % provision_mng_error) self.autoscaling_threads = ThreadPool(processes=1) self.autoscaling_threads.apply_async(self.scaler.do_provisioning, (response_time, cool_down, strategy)) config = self._configuration_get() config.autoscaling = True self._configuration_set(config) return HttpJsonResponse({'autoscaling': config.autoscaling}) except Exception as ex: self.logger.critical('Error when trying to stop the autoscaling mechanism: %s' % ex) return HttpErrorResponse("%s" % ex)
def get_node_info(self, kwargs): node_ids = [ str(node.id) for node in self.nodes ] exp_params = [('serviceNodeId', is_in_list(node_ids))] try: serviceNodeId = check_arguments(exp_params, kwargs) except Exception as ex: return HttpErrorResponse("%s" % ex) for node in self.nodes: if serviceNodeId == node.id: serviceNode = node break return HttpJsonResponse({ 'serviceNode': { 'id': serviceNode.id, 'ip': serviceNode.ip, 'vmid': serviceNode.vmid, 'cloud': serviceNode.cloud_name, 'role': serviceNode.role, 'logs': self.get_role_logs(serviceNode.role) } })
def test_check_arguments(self): ## No parameters exp_params = [] args = {} parsed_args = check_arguments(exp_params, args) self.assertEqual(parsed_args, []) # One single integer parameter exp_params = [('name1', is_int)] args = {'name1': 3} name1 = check_arguments(exp_params, args) self.assertEqual(name1, 3) # Missing one parameter exp_params = [('name1', is_int)] args = {} self.assertRaises(Exception, check_arguments, exp_params, args) # Wrong type exp_params = [('name1', is_int)] args = {'name1': 'value'} self.assertRaises(Exception, check_arguments, exp_params, args) # One single string parameter exp_params = [('name1', is_string)] args = {'name1': 'value'} name1 = check_arguments(exp_params, args) self.assertEqual(name1, 'value') exp_params = [('name1', is_string)] args = {'name1': u'value'} name1 = check_arguments(exp_params, args) self.assertEqual(name1, 'value') # An integer when a string was expected exp_params = [('name1', is_string)] args = {'name1': 3} self.assertRaises(Exception, check_arguments, exp_params, args) # A positive integer exp_params = [('name1', is_pos_int)] args = {'name1': 3} name1 = check_arguments(exp_params, args) self.assertEqual(name1, 3) # A positive integer exp_params = [('name1', is_pos_int)] args = {'name1': -3} self.assertRaises(Exception, check_arguments, exp_params, args) # A positive integer exp_params = [('name1', is_pos_int)] args = {'name1': 0} self.assertRaises(Exception, check_arguments, exp_params, args) # A positive or null integer exp_params = [('name1', is_pos_nul_int)] args = {'name1': 3} name1 = check_arguments(exp_params, args) self.assertEqual(name1, 3) # A positive or null integer exp_params = [('name1', is_pos_nul_int)] args = {'name1': -3} self.assertRaises(Exception, check_arguments, exp_params, args) # A positive or null integer exp_params = [('name1', is_pos_nul_int)] args = {'name1': 0} name1 = check_arguments(exp_params, args) self.assertEqual(name1, 0) # Extra unknown parameter exp_params = [('name1', is_int)] args = {'name1': 3, 'name2': 'value'} self.assertRaises(Exception, check_arguments, exp_params, args) # Default value exp_params = [('name1', is_int, 5)] args = {} name1 = check_arguments(exp_params, args) self.assertEqual(name1, 5) # Mix of mandatory args and optional ones exp_params = [('name1', is_int, 5), ('name2', is_int)] args = {'name2': 7} name1, name2 = check_arguments(exp_params, args) self.assertEqual(name1, 5) self.assertEqual(name2, 7) # Check uploaded file type exp_params = [('uploaded_file', is_uploaded_file)] filename = 'myfile.txt' filecontent = 'file' args = {'uploaded_file': FileUploadField(filename, filecontent)} uploaded_file = check_arguments(exp_params, args) self.assertEqual(uploaded_file.filename, filename) self.assertEqual(uploaded_file.file, filecontent) # Check in_list constraint exp_params = [('name1', is_in_list([3, 5, 6]))] args = {'name1': 6} name1 = check_arguments(exp_params, args) self.assertEqual(name1, 6) # Check not in_list constraint exp_params = [('name1', is_in_list([3, 5, 6]))] args = {'name1': 1} self.assertRaises(Exception, check_arguments, exp_params, args) # Check is_list constraint exp_params = [('name1', is_list)] mylist = [3, 'a'] args = {'name1': mylist} name1 = check_arguments(exp_params, args) self.assertEqual(name1, mylist) # Check is_dict constraint exp_params = [('name1', is_dict)] mydict = {'key': 3} args = {'name1': mydict} name1 = check_arguments(exp_params, args) self.assertEqual(name1, mydict) # Check is_dict2 constraint exp_params = [('name1', is_dict2(["key"]))] mydict = {'key': 3} args = {'name1': mydict} name1 = check_arguments(exp_params, args) self.assertEqual(name1, mydict) # Check is_dict2 constraint exp_params = [('name1', is_dict2(["key"], ["optkey"]))] mydict = {'key': 3} args = {'name1': mydict} name1 = check_arguments(exp_params, args) self.assertEqual(name1, mydict) # Check is_dict2 constraint: missing mandatory exp_params = [('name1', is_dict2(["key"], ["optkey"]))] mydict = {'optkey': 3} args = {'name1': mydict} self.assertRaisesRegexp(Exception, ".*expecting key.*", check_arguments, exp_params, args) # Check is_dict2 constraint: extra unknown key exp_params = [('name1', is_dict2(["key"], ["optkey"]))] mydict = {'key': 42, 'optkey': 3, 'newkey': 'great value'} args = {'name1': mydict} self.assertRaisesRegexp(Exception, ".*Unexpected key.*", check_arguments, exp_params, args) # Check is_list_dict constraint exp_params = [('name1', is_list_dict)] mylistdict = [{'key': 3}] args = {'name1': mylistdict} name1 = check_arguments(exp_params, args) self.assertEqual(name1, mylistdict) # Check is_list_dict2 constraint exp_params = [('name1', is_list_dict2(['key']))] mylistdict = [{'key': 3}, {'key': 56}] args = {'name1': mylistdict} name1 = check_arguments(exp_params, args) self.assertEqual(name1, mylistdict) # Check is_list_dict2 constraint exp_params = [('name1', is_list_dict2(['key']))] mylistdict = [{'key': 3}, {'key': 56, 'nokey': 42}] args = {'name1': mylistdict} self.assertRaisesRegexp(Exception, ".*Unexpected key.*", check_arguments, exp_params, args) # Try with a JSON conversion first # Check is_list constraint exp_params = [('name1', is_list)] mylist = unicode('[3, "a"]') mylist = json.loads(mylist) args = {'name1': mylist} name1 = check_arguments(exp_params, args) self.assertEqual(name1, mylist) # Check is_dict constraint exp_params = [('name1', is_dict)] mydict = unicode('{"key": 3}') mydict = json.loads(mydict) args = {'name1': mydict} name1 = check_arguments(exp_params, args) self.assertEqual(name1, mydict)