def load_dump(self, kwargs): self.logger.debug('Uploading mysql dump') if 'mysqldump_file' not in kwargs: return HttpErrorResponse(ManagerException(ManagerException.E_ARGS_MISSING, \ 'mysqldump_file').message) mysqldump_file = kwargs.pop('mysqldump_file') if len(kwargs) != 0: return HttpErrorResponse(ManagerException(ManagerException.E_ARGS_UNEXPECTED, \ detail='invalid number of arguments ').message) if not isinstance(mysqldump_file, FileUploadField): return HttpErrorResponse(ManagerException(ManagerException.E_ARGS_INVALID, \ detail='mysqldump_file should be a file').message) fd, filename = tempfile.mkstemp(dir='/tmp') fd = os.fdopen(fd, 'w') upload = mysqldump_file.file bytes = upload.read(2048) while len(bytes) != 0: fd.write(bytes) bytes = upload.read(2048) fd.close() # Get master # TODO: modify this when multiple masters masters = self.config.getMySQLmasters() try: for master in masters: client.load_dump(master.ip, self.config.AGENT_PORT, filename) except: self.logger.exception('load_dump: could not upload mysqldump_file ') self.state = self.S_ERROR return return HttpJsonResponse()
def get_node_info(self, kwargs): if 'serviceNodeId' not in kwargs: return HttpErrorResponse( ManagerException(ManagerException.E_ARGS_MISSING, 'serviceNodeId').message) serviceNodeId = kwargs.pop('serviceNodeId') if len(kwargs) != 0: return HttpErrorResponse( ManagerException(ManagerException.E_ARGS_UNEXPECTED, kwargs.keys()).message) config = self._configuration_get() if serviceNodeId not in config.serviceNodes: return HttpErrorResponse( ManagerException(ManagerException.E_ARGS_INVALID, detail='Invalid "serviceNodeId"').message) serviceNode = config.serviceNodes[serviceNodeId] return HttpJsonResponse({ 'serviceNode': { 'id': serviceNode.id, 'ip': serviceNode.ip, 'isRunningProxy': serviceNode.isRunningProxy, 'isRunningWeb': serviceNode.isRunningWeb, 'isRunningBackend': serviceNode.isRunningBackend, } })
def download_code_version(self, kwargs): if 'codeVersionId' not in kwargs: return HttpErrorResponse( ManagerException(ManagerException.E_ARGS_MISSING, 'codeVersionId').message) if isinstance(kwargs['codeVersionId'], dict): return HttpErrorResponse( ManagerException( ManagerException.E_ARGS_INVALID, detail='codeVersionId should be a string').message) codeVersion = kwargs.pop('codeVersionId') if len(kwargs) != 0: return HttpErrorResponse( ManagerException(ManagerException.E_ARGS_UNEXPECTED, kwargs.keys()).message) config = self._configuration_get() if codeVersion not in config.codeVersions: return HttpErrorResponse( ManagerException(ManagerException.E_ARGS_INVALID, detail='Invalid codeVersionId').message) filename = os.path.abspath(os.path.join(self.code_repo, codeVersion)) if not filename.startswith(self.code_repo + '/') or not os.path.exists(filename): return HttpErrorResponse( ManagerException(ManagerException.E_ARGS_INVALID, detail='Invalid codeVersionId').message) return HttpFileDownloadResponse( config.codeVersions[codeVersion].filename, filename)
def get_node_info(self, kwargs): """ HTTP GET method. Gets info of a specific node. :param param: serviceNodeId is a VMID of an existing service node. :type param: str :returns: HttpJsonResponse - JSON response with details about the node. :raises: ManagerException """ if 'serviceNodeId' not in kwargs: return HttpErrorResponse(ManagerException(E_ARGS_MISSING, 'serviceNodeId').message) serviceNodeId = kwargs.pop('serviceNodeId') if len(kwargs) != 0: return HttpErrorResponse(ManagerException(E_ARGS_UNEXPECTED, kwargs.keys()).message) if serviceNodeId not in self.config.serviceNodes: return HttpErrorResponse(ManagerException(E_ARGS_INVALID , \ "serviceNodeId" ,\ detail='Invalid "serviceNodeId"').message) serviceNode = self.config.getMySQLNode(serviceNodeId) return HttpJsonResponse({ 'serviceNode': { 'id': serviceNode.id, 'ip': serviceNode.ip, 'isMaster': serviceNode.isMaster, 'isSlave': serviceNode.isSlave } })
def upload_code_version(self, kwargs): if 'code' not in kwargs: return HttpErrorResponse( ManagerException(ManagerException.E_ARGS_MISSING, 'code').message) code = kwargs.pop('code') if 'description' in kwargs: description = kwargs.pop('description') else: description = '' if len(kwargs) != 0: return HttpErrorResponse( ManagerException(ManagerException.E_ARGS_UNEXPECTED, kwargs.keys()).message) if not isinstance(code, FileUploadField): return HttpErrorResponse( ManagerException( ManagerException.E_ARGS_INVALID, detail='codeVersionId should be a file').message) config = self._configuration_get() fd, name = tempfile.mkstemp(prefix='code-', dir=self.code_repo) fd = os.fdopen(fd, 'w') upload = code.file codeVersionId = os.path.basename(name) bytes = upload.read(2048) while len(bytes) != 0: fd.write(bytes) bytes = upload.read(2048) fd.close() arch = archive_open(name) if arch == None: os.remove(name) return HttpErrorResponse( ManagerException(ManagerException.E_ARGS_INVALID, detail='Invalid archive format').message) for fname in archive_get_members(arch): if fname.startswith('/') or fname.startswith('..'): archive_close(arch) os.remove(name) return HttpErrorResponse( ManagerException( ManagerException.E_ARGS_INVALID, detail= 'Absolute file names are not allowed in archive members' ).message) archive_close(arch) config.codeVersions[codeVersionId] = CodeVersion( codeVersionId, os.path.basename(code.filename), archive_get_type(name), description=description) self._configuration_set(config) return HttpJsonResponse( {'codeVersionId': os.path.basename(codeVersionId)})
def shutdown(self, kwargs): if len(kwargs) != 0: return HttpErrorResponse( ManagerException(ManagerException.E_ARGS_UNEXPECTED, kwargs.keys()).message) dstate = self._state_get() if dstate != self.S_RUNNING: return HttpErrorResponse( ManagerException(ManagerException.E_STATE_ERROR).message) config = self._configuration_get() self._state_set(self.S_EPILOGUE, msg='Shutting down') Thread(target=self.do_shutdown, args=[config]).start() return HttpJsonResponse({'state': self.S_EPILOGUE})
def list_authorized_keys(self, kwargs): if len(kwargs) != 0: return HttpErrorResponse( ManagerException(ManagerException.E_ARGS_UNEXPECTED, kwargs.keys()).message) return HttpJsonResponse({'authorizedKeys': git.get_authorized_keys()})
def shutdown(self, kwargs): """ HTTP POST method. Shuts down the manager service. :returns: HttpJsonResponse - JSON response with details about the status of a manager node: . ManagerException if something went wrong. :raises: ManagerException """ if len(kwargs) != 0: return HttpErrorResponse(ManagerException(E_ARGS_UNEXPECTED, kwargs.keys()).message) if self.state != self.S_RUNNING: return HttpErrorResponse(ManagerException(E_STATE_ERROR).message) self.state = self.S_EPILOGUE Thread(target=self._do_shutdown, args=[]).start() return HttpJsonResponse({'state': self.S_EPILOGUE})
def startup(self, kwargs): ''' Starts the service - it will start and configure a Galera master ''' self.logger.debug("Entering GaleraServerManager startup") if self.state != self.S_INIT and self.state != self.S_STOPPED: return HttpErrorResponse(ManagerException(E_STATE_ERROR).message) self.state = self.S_PROLOGUE Thread(target=self._do_startup, kwargs=kwargs).start() return HttpJsonResponse({'state': self.S_PROLOGUE})
def get_service_performance(self, kwargs): if len(kwargs) != 0: return HttpErrorResponse( ManagerException(ManagerException.E_ARGS_UNEXPECTED, kwargs.keys()).message) return HttpJsonResponse({ 'request_rate': 0, 'error_rate': 0, 'throughput': 0, 'response_time': 0, })
def startup(self, kwargs): config = self._configuration_get() dstate = self._state_get() if dstate != self.S_INIT and dstate != self.S_STOPPED: return HttpErrorResponse( ManagerException(ManagerException.E_STATE_ERROR).message) if config.proxy_count == 1 \ and (config.web_count == 0 or config.backend_count == 0):# at least one is packed if config.web_count == 0 and config.backend_count == 0: # packed serviceNodeKwargs = [{ 'runProxy': True, 'runWeb': True, 'runBackend': True }] elif config.web_count == 0 and config.backend_count > 0: # web packed, backend separated serviceNodeKwargs = [{ 'runBackend': True } for _ in range(config.backend_count)] serviceNodeKwargs.append({'runProxy': True, 'runWeb': True}) elif config.web_count > 0 and config.backend_count == 0: # proxy separated, backend packed serviceNodeKwargs = [{ 'runWeb': True } for _ in range(config.web_count)] serviceNodeKwargs.append({ 'runProxy': True, 'runBackend': True }) else: if config.web_count < 1: config.web_count = 1 # have to have at least one web if config.backend_count < 1: config.backend_count = 1 # have to have at least one backend serviceNodeKwargs = [{ 'runProxy': True } for _ in range(config.proxy_count)] serviceNodeKwargs.extend([{ 'runWeb': True } for _ in range(config.web_count)]) serviceNodeKwargs.extend([{ 'runBackend': True } for _ in range(config.backend_count)]) self._state_set(self.S_PROLOGUE, msg='Starting up') kwargs['config'] = config kwargs['serviceNodeKwargs'] = serviceNodeKwargs Thread(target=self.do_startup, kwargs=kwargs).start() return HttpJsonResponse({'state': self.S_PROLOGUE})
def upload_script(self, kwargs, filename): """Write the file uploaded in kwargs['script'] to filesystem. Return the script absoulte path on success, HttpErrorResponse on failure. """ self.logger.debug("upload_script: called with filename=%s" % filename) # Check if the required argument 'script' is present if 'script' not in kwargs: return HttpErrorResponse( ManagerException(ManagerException.E_ARGS_MISSING, 'script').message) script = kwargs.pop('script') # Check if any trailing parameter has been submitted if len(kwargs) != 0: return HttpErrorResponse( ManagerException(ManagerException.E_ARGS_UNEXPECTED, kwargs.keys()).message) # Script has to be a FileUploadField if not isinstance(script, FileUploadField): return HttpErrorResponse( ManagerException(ManagerException.E_ARGS_INVALID, detail='script should be a file').message) basedir = self.config_parser.get('manager', 'CONPAAS_HOME') fullpath = os.path.join(basedir, filename) # Write the uploaded script to filesystem open(fullpath, 'w').write(script.file.read()) self.logger.debug( "upload_script: script uploaded successfully to '%s'" % fullpath) # Return the script absolute path return fullpath
def upload_authorized_key(self, kwargs): if 'key' not in kwargs: return HttpErrorResponse( ManagerException(ManagerException.E_ARGS_MISSING, 'key').message) key = kwargs.pop('key') if len(kwargs) != 0: return HttpErrorResponse( ManagerException(ManagerException.E_ARGS_UNEXPECTED, kwargs.keys()).message) if not isinstance(key, FileUploadField): return HttpErrorResponse( ManagerException(ManagerException.E_ARGS_INVALID, detail='key should be a file').message) key_lines = key.file.readlines() num_added = git.add_authorized_keys(key_lines) return HttpJsonResponse( {'outcome': "%s keys added to authorized_keys" % num_added})
def list_nodes(self, kwargs): if len(kwargs) != 0: return HttpErrorResponse( ManagerException(ManagerException.E_ARGS_UNEXPECTED, kwargs.keys()).message) dstate = self._state_get() if dstate != self.S_RUNNING and dstate != self.S_ADAPTING: return HttpErrorResponse( ManagerException(ManagerException.E_STATE_ERROR).message) config = self._configuration_get() return HttpJsonResponse({ 'proxy': [serviceNode.id for serviceNode in config.getProxyServiceNodes()], 'web': [serviceNode.id for serviceNode in config.getWebServiceNodes()], 'backend': [ serviceNode.id for serviceNode in config.getBackendServiceNodes() ] })
def get_service_performance(self, kwargs): ''' HTTP GET method. Placeholder for obtaining performance metrics. :param kwargs: Additional parameters. :type kwargs: dict :returns: HttpJsonResponse -- returns metrics ''' if len(kwargs) != 0: return HttpErrorResponse(ManagerException(E_ARGS_UNEXPECTED, kwargs.keys()).message) return HttpJsonResponse({ 'request_rate': 0, 'error_rate': 0, 'throughput': 0, 'response_time': 0, })
def git_push_hook(self, kwargs): if len(kwargs) != 0: return HttpErrorResponse( ManagerException(ManagerException.E_ARGS_UNEXPECTED, kwargs.keys()).message) config = self._configuration_get() repo = git.DEFAULT_CODE_REPO codeVersionId = git.git_code_version(repo) config.codeVersions[codeVersionId] = CodeVersion( id=codeVersionId, filename=codeVersionId, atype="git", description=git.git_last_description(repo)) self._configuration_set(config) return HttpJsonResponse({'codeVersionId': codeVersionId})
def list_nodes(self, kwargs): """ HTTP GET method. Uses :py:meth:`IaaSClient.listVMs()` to get list of all Service nodes. For each service node it gets it checks if it is in servers list. If some of them are missing they are removed from the list. Returns list of all service nodes. :returns: HttpJsonResponse - JSON response with the list of services :raises: HttpErrorResponse """ if len(kwargs) != 0: return HttpErrorResponse(ManagerException(E_ARGS_UNEXPECTED, kwargs.keys()).message) return HttpJsonResponse({ 'masters': [ node.id for node in self.config.getMySQLmasters() ], 'slaves': [ node.id for node in self.config.getMySQLslaves() ] })
def list_code_versions(self, kwargs): if len(kwargs) != 0: return HttpErrorResponse( ManagerException(ManagerException.E_ARGS_UNEXPECTED, kwargs.keys()).message) config = self._configuration_get() versions = [] for version in config.codeVersions.values(): item = { 'codeVersionId': version.id, 'filename': version.filename, 'description': version.description, 'time': version.timestamp } if version.id == config.currentCodeVersion: item['current'] = True versions.append(item) versions.sort(cmp=(lambda x, y: cmp(x['time'], y['time'])), reverse=True) return HttpJsonResponse({'codeVersions': versions})
def remove_nodes(self, kwargs): config = self._configuration_get() backend = 0 web = 0 proxy = 0 if 'backend' in kwargs: if not isinstance(kwargs['backend'], int): return HttpErrorResponse( ManagerException( ManagerException.E_ARGS_INVALID, detail='Expected an integer value for "backend"'). message) backend = int(kwargs.pop('backend')) if backend < 0: return HttpErrorResponse( ManagerException( ManagerException.E_ARGS_INVALID, detail='Expected a positive integer value for "backend"' ).message) if 'web' in kwargs: if not isinstance(kwargs['web'], int): return HttpErrorResponse( ManagerException( ManagerException.E_ARGS_INVALID, detail='Expected an integer value for "web"').message) web = int(kwargs.pop('web')) if web < 0: return HttpErrorResponse( ManagerException( ManagerException.E_ARGS_INVALID, detail='Expected a positive integer value for "web"'). message) if 'proxy' in kwargs: if not isinstance(kwargs['proxy'], int): return HttpErrorResponse( ManagerException( ManagerException.E_ARGS_INVALID, detail='Expected an integer value for "proxy"').message ) proxy = int(kwargs.pop('proxy')) if proxy < 0: return HttpErrorResponse( ManagerException( ManagerException.E_ARGS_INVALID, detail='Expected a positive integer value for "proxy"' ).message) if (backend + web + proxy) < 1: return HttpErrorResponse( ManagerException( ManagerException.E_ARGS_MISSING, ['backend', 'web', 'proxy'], detail='Need a positive value for at least one').message) if len(kwargs) != 0: return HttpErrorResponse( ManagerException(ManagerException.E_ARGS_UNEXPECTED, kwargs.keys()).message) if config.proxy_count - proxy < 1: return HttpErrorResponse( ManagerException( ManagerException.E_ARGS_INVALID, detail='Not enough proxy nodes will be left').message) if config.web_count - web < 1 and config.proxy_count - proxy > 1: return HttpErrorResponse( ManagerException( ManagerException.E_ARGS_INVALID, detail='Not enough web nodes will be left').message) if config.web_count - web < 0: return HttpErrorResponse( ManagerException( ManagerException.E_ARGS_INVALID, detail='Cannot remove_nodes that many web nodes').message) if config.backend_count - backend < 1 and config.proxy_count - proxy > 1: return HttpErrorResponse( ManagerException( ManagerException.E_ARGS_INVALID, detail='Not enough backend nodes will be left').message) if config.backend_count - backend < 0: return HttpErrorResponse( ManagerException( ManagerException.E_ARGS_INVALID, detail='Cannot remove_nodes that many backend nodes'). message) dstate = self._state_get() if dstate != self.S_RUNNING: return HttpErrorResponse( ManagerException(ManagerException.E_STATE_ERROR).message) self._state_set( self.S_ADAPTING, msg='Going to remove_nodes proxy=%d, web=%d, backend=%d' % (proxy, web, backend)) Thread(target=self.do_remove_nodes, args=[config, proxy, web, backend]).start() return HttpJsonResponse()
def get_service_history(self, kwargs): if len(kwargs) != 0: return HttpErrorResponse( ManagerException(ManagerException.E_ARGS_UNEXPECTED, kwargs.keys()).message) return HttpJsonResponse({'state_log': self.state_log})
def invalid_arg(msg): return HttpErrorResponse( ManagerException(ManagerException.E_ARGS_INVALID, detail=msg).message)
def add_nodes(self, kwargs): config = self._configuration_get() dstate = self._state_get() if dstate != self.S_RUNNING: return HttpErrorResponse( ManagerException(ManagerException.E_STATE_ERROR).message) backend = 0 web = 0 proxy = 0 vm_backend_type = None vm_web_type = None if 'backend' in kwargs: if not isinstance(kwargs['backend'], int): return HttpErrorResponse( ManagerException( ManagerException.E_ARGS_INVALID, detail='Expected an integer value for "backend"'). message) backend = int(kwargs.pop('backend')) if backend < 0: return HttpErrorResponse( ManagerException( ManagerException.E_ARGS_INVALID, detail='Expected a positive integer value for "backend"' ).message) if 'web' in kwargs: if not isinstance(kwargs['web'], int): return HttpErrorResponse( ManagerException( ManagerException.E_ARGS_INVALID, detail='Expected an integer value for "web"').message) web = int(kwargs.pop('web')) if web < 0: return HttpErrorResponse( ManagerException( ManagerException.E_ARGS_INVALID, detail='Expected a positive integer value for "web"'). message) if 'proxy' in kwargs: if not isinstance(kwargs['proxy'], int): return HttpErrorResponse( ManagerException( ManagerException.E_ARGS_INVALID, detail='Expected an integer value for "proxy"').message ) proxy = int(kwargs.pop('proxy')) if proxy < 0: return HttpErrorResponse( ManagerException( ManagerException.E_ARGS_INVALID, detail='Expected a positive integer value for "proxy"' ).message) if (backend + web + proxy) < 1: return HttpErrorResponse( ManagerException( ManagerException.E_ARGS_MISSING, ['backend', 'web', 'proxy'], detail='Need a positive value for at least one').message) ####### ADDED SCALING V2: ###################### if 'vm_backend_instance' in kwargs: self.logger.info('VM BACKEND INSTACE: %s' % str(kwargs['vm_backend_instance'])) if not isinstance(str(kwargs['vm_backend_instance']), basestring): return HttpErrorResponse( ManagerException( ManagerException.E_ARGS_INVALID, detail= 'Expected a string value for "vm_backend_instance"'). message) vm_backend_type = kwargs.pop('vm_backend_instance') if len(vm_backend_type) <= 0: return HttpErrorResponse( ManagerException( ManagerException.E_ARGS_INVALID, detail= 'Expected a string value for "vm_backend_instance"'). message) if 'vm_web_instance' in kwargs: self.logger.info('VM WEB INSTACE: %s' % str(kwargs['vm_web_instance'])) if not isinstance(str(kwargs['vm_web_instance']), basestring): return HttpErrorResponse( ManagerException( ManagerException.E_ARGS_INVALID, detail='Expected a string value for "vm_web_instance"' ).message) vm_web_type = kwargs.pop('vm_web_instance') if len(vm_web_type) <= 0: return HttpErrorResponse( ManagerException( ManagerException.E_ARGS_INVALID, detail='Expected a string value for "vm_web_instance"' ).message) ############################# if (proxy + config.proxy_count) > 1 and ( (web + config.web_count) == 0 or (backend + config.backend_count) == 0): return HttpErrorResponse( ManagerException( ManagerException.E_ARGS_INVALID, detail= 'Cannot add more proxy servers without at least one "web" and one "backend"' ).message) self._state_set( self.S_ADAPTING, msg= 'Going to add proxy=%d, web=%d, backend=%d, vm_backend_type=%s, vm_web_type=%s, cloud=%s' % (proxy, web, backend, str(vm_backend_type), str(vm_web_type), str(kwargs['cloud']))) Thread(target=self.do_add_nodes, args=[ config, proxy, web, backend, kwargs['cloud'], vm_backend_type, vm_web_type ]).start() return HttpJsonResponse()
def update_nodes_weight(self, kwargs): config = self._configuration_get() dstate = self._state_get() if dstate != self.S_RUNNING: self.logger.critical('update_nodes_weight function: STATE_ERROR') return HttpErrorResponse( ManagerException(ManagerException.E_STATE_ERROR).message) self.logger.debug('Received request to update nodes weight...') backend_weights = {} web_weights = {} if 'web' in kwargs: if not isinstance(kwargs['web'], dict): return HttpErrorResponse( ManagerException( ManagerException.E_ARGS_INVALID, detail='Expected a dictionary value for "web"').message ) web_weights = kwargs.pop('web') if 'backend' in kwargs: if not isinstance(kwargs['backend'], dict): return HttpErrorResponse( ManagerException( ManagerException.E_ARGS_INVALID, detail='Expected a dictionary value for "backend"'). message) backend_weights = kwargs.pop('backend') for web_id in web_weights: if web_id not in config.serviceNodes: return HttpErrorResponse( ManagerException( ManagerException.E_ARGS_INVALID, detail='The web node ID does not exist').message) self.logger.debug('Updating web weight for node: %s to: %s ' % (str(web_id), str(web_weights[web_id]))) config.serviceNodes[web_id].weightWeb = int(web_weights[web_id]) for backend_id in backend_weights: if backend_id not in config.serviceNodes: return HttpErrorResponse( ManagerException( ManagerException.E_ARGS_INVALID, detail='The backend node ID does not exist').message) self.logger.debug( 'Updating backend weight for node: %s to: %s ' % (str(backend_id), str(backend_weights[backend_id]))) config.serviceNodes[backend_id].weightBackend = int( backend_weights[backend_id]) self.logger.debug("Result of updating node weights: %s" % str(config.serviceNodes)) for web_id in web_weights: if web_id not in config.serviceNodes: return HttpErrorResponse( ManagerException( ManagerException.E_ARGS_INVALID, detail='The web node ID does not exist').message) config.serviceNodes[web_id].webWeight = int(web_weights[web_id]) config.update_mappings() self._update_proxy( config, [i for i in config.serviceNodes.values() if i.isRunningProxy]) self._configuration_set(config) return HttpJsonResponse()
def add_nodes(self, kwargs): config = self._configuration_get() dstate = self._state_get() if dstate != self.S_RUNNING: return HttpErrorResponse( ManagerException(ManagerException.E_STATE_ERROR).message) backend = 0 web = 0 proxy = 0 if 'backend' in kwargs: if not isinstance(kwargs['backend'], int): return HttpErrorResponse( ManagerException( ManagerException.E_ARGS_INVALID, detail='Expected an integer value for "backend"'). message) backend = int(kwargs.pop('backend')) if backend < 0: return HttpErrorResponse( ManagerException( ManagerException.E_ARGS_INVALID, detail='Expected a positive integer value for "backend"' ).message) if 'web' in kwargs: if not isinstance(kwargs['web'], int): return HttpErrorResponse( ManagerException( ManagerException.E_ARGS_INVALID, detail='Expected an integer value for "web"').message) web = int(kwargs.pop('web')) if web < 0: return HttpErrorResponse( ManagerException( ManagerException.E_ARGS_INVALID, detail='Expected a positive integer value for "web"'). message) if 'proxy' in kwargs: if not isinstance(kwargs['proxy'], int): return HttpErrorResponse( ManagerException( ManagerException.E_ARGS_INVALID, detail='Expected an integer value for "proxy"').message ) proxy = int(kwargs.pop('proxy')) if proxy < 0: return HttpErrorResponse( ManagerException( ManagerException.E_ARGS_INVALID, detail='Expected a positive integer value for "proxy"' ).message) if (backend + web + proxy) < 1: return HttpErrorResponse( ManagerException( ManagerException.E_ARGS_MISSING, ['backend', 'web', 'proxy'], detail='Need a positive value for at least one').message) if (proxy + config.proxy_count) > 1 and ( (web + config.web_count) == 0 or (backend + config.backend_count) == 0): return HttpErrorResponse( ManagerException( ManagerException.E_ARGS_INVALID, detail= 'Cannot add more proxy servers without at least one "web" and one "backend"' ).message) self._state_set(self.S_ADAPTING, msg='Going to add proxy=%d, web=%d, backend=%d' % (proxy, web, backend)) Thread(target=self.do_add_nodes, args=[config, proxy, web, backend, kwargs['cloud']]).start() return HttpJsonResponse()