def __init__(self, conf, **kwargs): BaseManager.__init__(self, conf) self.logger.debug("Entering MySQLServerManager initialization") self.controller.generate_context('mysql') self.controller.config_clouds({ "mem" : "512", "cpu" : "1" }) self.config = Configuration(conf) self.logger.debug("Leaving MySQLServer initialization") # The unique id that is used to start the master/slave self.id = 0
def __init__(self, conf, **kwargs): BaseManager.__init__(self, conf) self.logger.debug("Entering MySQLServerManager initialization") # default value for mysql volume size self.mysql_volume_size = 1024 #(genc): this is ignored at the moment # self.controller.config_clouds({"mem": "512", "cpu": "1"}) self.root_pass = None self.config = Configuration(conf, self.logger) self.logger.debug("Leaving MySQLServer initialization")
class MySQLManager(BaseManager): # MySQL Galera node types ROLE_MYSQL = 'mysql' # regular node running a mysqld daemon ROLE_GLB = 'glb' # load balancer running a glbd daemon def __init__(self, conf, **kwargs): BaseManager.__init__(self, conf) self.logger.debug("Entering MySQLServerManager initialization") # default value for mysql volume size self.mysql_volume_size = 1024 #(genc): this is ignored at the moment # self.controller.config_clouds({"mem": "512", "cpu": "1"}) self.root_pass = None self.config = Configuration(conf, self.logger) self.logger.debug("Leaving MySQLServer initialization") def get_service_type(self): return 'mysql' def get_node_roles(self): return [ self.ROLE_MYSQL, self.ROLE_GLB ] def get_default_role(self): return self.ROLE_MYSQL def get_role_sninfo(self, role, cloud): if role == self.ROLE_MYSQL: return self.get_standard_sninfo_with_volume( role, cloud, 'mysql-%(vm_id)s', self.mysql_volume_size) else: return BaseManager.get_role_sninfo(self, role, cloud) def get_role_logs(self, role): logs = BaseManager.get_role_logs(self, role) if role == self.ROLE_MYSQL: logs.extend([{'filename': 'mysql.log', 'description': 'MySQL log', 'path': '/var/cache/cpsagent/mysql.log'}]); return logs def get_context_replacement(self): if not self.root_pass: # self.root_pass='******' self.root_pass = ''.join([choice(string.letters + string.digits) for i in range(10)]) # self.logger.debug('setting context to %s' % dict(mysql_username='******', mysql_password=self.root_pass)) return dict(mysql_username='******', mysql_password=str(self.root_pass)) def on_start(self, nodes): succ = self._start_mysqld(nodes) self.config.addMySQLServiceNodes(nodes) return succ def _start_mysqld(self, nodes): dev_name = None existing_nodes = self.config.get_nodes_addr() for serviceNode in nodes: try: agent.start_mysqld(serviceNode.ip, self.config.AGENT_PORT, existing_nodes, serviceNode.volumes[0].dev_name) except AgentException, ex: self.logger.exception('Failed to start MySQL node %s: %s' % (str(serviceNode), ex)) raise try: glb_nodes = self.config.get_glb_nodes() self.logger.debug('MySQL nodes already active: %s' % glb_nodes) nodesIp=[] nodesIp = ["%s:%s" % (node.ip, self.config.MYSQL_PORT) # FIXME: find real mysql port instead of default 3306 for node in nodes] for glb in glb_nodes: agent.add_glbd_nodes(glb.ip, self.config.AGENT_PORT, nodesIp) return True except Exception as ex: self.logger.exception('Failed to configure new GLB node: %s' % ex) raise
class MySQLManager(BaseManager): """ Initializes :py:attr:`config` using Config and sets :py:attr:`state` to :py:attr:`S_INIT` :param conf: Configuration file. :type conf: str :type conf: boolean """ def __init__(self, conf, **kwargs): BaseManager.__init__(self, conf) self.logger.debug("Entering MySQLServerManager initialization") self.controller.generate_context('mysql') self.controller.config_clouds({ "mem" : "512", "cpu" : "1" }) self.config = Configuration(conf) self.logger.debug("Leaving MySQLServer initialization") # The unique id that is used to start the master/slave self.id = 0 self.root_pass = None def _do_startup(self, cloud): ''' Starts up the service. The first node will be the MYSQL master. The next nodes will be slaves to this master. ''' startCloud = self._init_cloud(cloud) #TODO: Get any existing configuration (if the service was stopped and restarted) self.logger.debug('do_startup: Going to request one new node') # Generate a password for root # TODO: send a username? self.root_pass = ''.join([choice(string.letters + string.digits) for i in range(10)]) self.controller.add_context_replacement(dict(mysql_username='******', \ mysql_password=self.root_pass),cloud=startCloud) try: node_instances = self.controller.create_nodes(1, client.check_agent_process, self.config.AGENT_PORT, startCloud) self._start_master(node_instances) self.config.addMySQLServiceNodes(nodes=node_instances, isMaster=True) except: self.controller.delete_nodes(node_instances) self.logger.exception('do_startup: Failed to request a new node on cloud %s' % cloud) self.state = self.S_STOPPED return self.state = self.S_RUNNING def _start_master(self, nodes): for serviceNode in nodes: try: client.create_master(serviceNode.ip, self.config.AGENT_PORT, self._get_server_id()) except client.AgentException: self.logger.exception('Failed to start MySQL Master at node %s' % str(serviceNode)) self.state = self.S_ERROR raise def _start_slave(self, nodes, master): slaves = {} for serviceNode in nodes: slaves[str(self._get_server_id())] = {'ip':serviceNode.ip, 'port':self.config.AGENT_PORT} try: self.logger.debug('create_slave for master.ip = %s' % master) client.create_slave(master.ip, self.config.AGENT_PORT, slaves) except client.AgentException: self.logger.exception('Failed to start MySQL Slave at node %s' % str(serviceNode)) self.state = self.S_ERROR raise @expose('GET') 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() ] }) @expose('GET') 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 } }) @expose('POST') def add_nodes(self, kwargs): """ HTTP POST method. Creates new node and adds it to the list of existing nodes in the manager. Makes internal call to :py:meth:`createServiceNodeThread`. :param kwargs: number of nodes to add. :type param: str :returns: HttpJsonResponse - JSON response with details about the node. :raises: ManagerException """ if self.state != self.S_RUNNING: return HttpErrorResponse('ERROR: Wrong state to add_nodes') if not 'slaves' in kwargs: return HttpErrorResponse('ERROR: Required argument doesn\'t exist') if not isinstance(kwargs['slaves'], int): return HttpErrorResponse('ERROR: Expected an integer value for "count"') count = int(kwargs.pop('slaves')) self.state = self.S_ADAPTING Thread(target=self._do_add_nodes, args=[count, kwargs['cloud']]).start() return HttpJsonResponse() # TODO: also specify the master for which to add slaves def _do_add_nodes(self, count, cloud): # Get the master masters = self.config.getMySQLmasters() startCloud = self._init_cloud(cloud) # Configure the nodes as slaves #TODO: modify this when multiple masters try: node_instances = self.controller.create_nodes(count, client.check_agent_process, self.config.AGENT_PORT, startCloud) for master in masters: self._start_slave(node_instances, master) self.config.addMySQLServiceNodes(nodes=node_instances, isSlave=True) except: self.logger.exception('_do_add_nodes: Could not start slave') self.state = self.S_ERROR return self.state = self.S_RUNNING def _get_server_id(self): self.id = self.id + 1 return self.id @expose('GET') 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, }) @expose('POST') def remove_nodes(self, kwargs): if self.state != self.S_RUNNING: self.logger.debug('Wrong state to remove nodes') return HttpErrorResponse('ERROR: Wrong state to remove_nodes') if not 'slaves' in kwargs: return HttpErrorResponse('ERROR: Required argument doesn\'t exist') if not isinstance(kwargs['slaves'], int): return HttpErrorResponse('ERROR: Expected an integer value for "count"') count = int(kwargs.pop('slaves')) if count > len(self.config.getMySQLslaves()): return HttpErrorResponse('ERROR: Cannot remove so many nodes') self.state = self.S_ADAPTING Thread(target=self._do_remove_nodes, args=[count]).start() return HttpJsonResponse() def _do_remove_nodes(self, count): nodes = self.config.getMySQLslaves()[:count] self.controller.delete_nodes(nodes) self.config.remove_nodes(nodes) self.state = self.S_RUNNING return HttpJsonResponse() @expose('GET') def get_service_info(self, kwargs): if len(kwargs) != 0: return HttpErrorResponse('ERROR: Arguments unexpected') return HttpJsonResponse({'state': self.state, 'type': 'mysql'}) @expose('POST') 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 _do_shutdown(self): ''' Shuts down the service. ''' #self._stop_slaves( config.getProxyServiceNodes()) #self._stop_masters(config, config.getWebServiceNodes()) self.controller.delete_nodes(self.config.serviceNodes.values()) self.config.serviceNodes = {} self.state = self.S_STOPPED @expose('GET') def get_password(self, kwargs): if len(kwargs) != 0: return HttpErrorResponse('ERROR: Arguments unexpected') return HttpJsonResponse(self.root_pass) @expose('POST') def set_password(self, kwargs): self.logger.debug('Setting password') if self.state != self.S_RUNNING: self.logger.debug('Service not runnning') return HttpErrorResponse('ERROR: Service not running') if not 'user' in kwargs: return HttpErrorResponse('ERROR: Required argument \'user\' doesn\'t exist') if not 'password' in kwargs: return HttpErrorResponse('ERROR: Required argument \'password\' doesn\'t exist') # Get the master masters = self.config.getMySQLmasters() #TODO: modify this when multiple masters try: for master in masters: client.set_password(master.ip, self.config.AGENT_PORT, kwargs['user'], kwargs['password']) except: e = sys.exc_info()[1] self.logger.exception('set_password: Could not set password: %s' % e) self.state = self.S_ERROR return HttpErrorResponse('Failed to set password') else: self.root_pass = kwargs['password'] return HttpJsonResponse() @expose('GET') def sqldump(self, kwargs): if self.state != self.S_RUNNING: return HttpErrorResponse('ERROR: Wrong state to call sqldump') master_ip = self.config.getMySQLmasters()[0].ip cmd = 'mysqldump -u mysqldb -h %s --password=%s -A' % (master_ip, self.root_pass) out, err = run_cmd(cmd) if err: return HttpErrorResponse(err) return HttpJsonResponse(out) @expose('UPLOAD') 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()
class MySQLManager(BaseManager): """ Initializes :py:attr:`config` using Config and sets :py:attr:`state` to :py:attr:`S_INIT` :param conf: Configuration file. :type conf: str :type conf: boolean """ def __init__(self, conf, **kwargs): BaseManager.__init__(self, conf) self.logger.debug("Entering MySQLServerManager initialization") self.controller.generate_context('mysql') self.controller.config_clouds({ "mem" : "512", "cpu" : "1" }) self.config = Configuration(conf) self.logger.debug("Leaving MySQLServer initialization") # The unique id that is used to start the master/slave self.id = 0 self.root_pass = None def _do_startup(self, cloud): ''' Starts up the service. The first node will be the MYSQL master. The next nodes will be slaves to this master. ''' startCloud = self._init_cloud(cloud) #TODO: Get any existing configuration (if the service was stopped and restarted) self.logger.debug('do_startup: Going to request one new node') # Generate a password for root # TODO: send a username? self.root_pass = ''.join([choice(string.letters + string.digits) for i in range(10)]) self.controller.add_context_replacement(dict(mysql_username='******', \ mysql_password=self.root_pass),cloud=startCloud) try: node_instances = self.controller.create_nodes(1, client.check_agent_process, self.config.AGENT_PORT, startCloud) self._start_master(node_instances) self.config.addMySQLServiceNodes(nodes=node_instances, isMaster=True) except: self.logger.exception('do_startup: Failed to request a new node on cloud %s' % cloud) self.state = self.S_STOPPED return self.state = self.S_RUNNING def _start_master(self, nodes): for serviceNode in nodes: try: client.create_master(serviceNode.ip, self.config.AGENT_PORT, self._get_server_id()) except client.AgentException: self.logger.exception('Failed to start MySQL Master at node %s' % str(serviceNode)) self.state = self.S_ERROR raise def _start_slave(self, nodes, master): slaves = {} for serviceNode in nodes: slaves[str(self._get_server_id())] = {'ip':serviceNode.ip, 'port':self.config.AGENT_PORT} try: self.logger.debug('create_slave for master.ip = %s' % master) client.create_slave(master.ip, self.config.AGENT_PORT, slaves) except client.AgentException: self.logger.exception('Failed to start MySQL Slave at node %s' % str(serviceNode)) self.state = self.S_ERROR raise @expose('GET') 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() ] }) @expose('GET') 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 } }) @expose('POST') def add_nodes(self, kwargs): """ HTTP POST method. Creates new node and adds it to the list of existing nodes in the manager. Makes internal call to :py:meth:`createServiceNodeThread`. :param kwargs: number of nodes to add. :type param: str :returns: HttpJsonResponse - JSON response with details about the node. :raises: ManagerException """ if self.state != self.S_RUNNING: return HttpErrorResponse('ERROR: Wrong state to add_nodes') if not 'slaves' in kwargs: return HttpErrorResponse('ERROR: Required argument doesn\'t exist') if not isinstance(kwargs['slaves'], int): return HttpErrorResponse('ERROR: Expected an integer value for "count"') count = int(kwargs.pop('slaves')) self.state = self.S_ADAPTING Thread(target=self._do_add_nodes, args=[count, kwargs['cloud']]).start() return HttpJsonResponse() # TODO: also specify the master for which to add slaves def _do_add_nodes(self, count, cloud): # Get the master masters = self.config.getMySQLmasters() startCloud = self._init_cloud(cloud) # Configure the nodes as slaves #TODO: modify this when multiple masters try: node_instances = self.controller.create_nodes(count, client.check_agent_process, self.config.AGENT_PORT, startCloud) for master in masters: self._start_slave(node_instances, master) self.config.addMySQLServiceNodes(nodes=node_instances, isSlave=True) except: self.logger.exception('_do_add_nodes: Could not start slave') self.state = self.S_ERROR return self.state = self.S_RUNNING def _get_server_id(self): self.id = self.id + 1 return self.id @expose('GET') 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, }) @expose('POST') def remove_nodes(self, kwargs): if self.state != self.S_RUNNING: self.logger.debug('Wrong state to remove nodes') return HttpErrorResponse('ERROR: Wrong state to remove_nodes') if not 'slaves' in kwargs: return HttpErrorResponse('ERROR: Required argument doesn\'t exist') if not isinstance(kwargs['slaves'], int): return HttpErrorResponse('ERROR: Expected an integer value for "count"') count = int(kwargs.pop('slaves')) if count > len(self.config.getMySQLslaves()): return HttpErrorResponse('ERROR: Cannot remove so many nodes') self.state = self.S_ADAPTING Thread(target=self._do_remove_nodes, args=[count]).start() return HttpJsonResponse() def _do_remove_nodes(self, count): nodes = self.config.getMySQLslaves()[:count] self.controller.delete_nodes(nodes) self.config.remove_nodes(nodes) self.state = self.S_RUNNING return HttpJsonResponse() @expose('GET') def get_service_info(self, kwargs): if len(kwargs) != 0: return HttpErrorResponse('ERROR: Arguments unexpected') return HttpJsonResponse({'state': self.state, 'type': 'mysql'}) @expose('POST') 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 _do_shutdown(self): ''' Shuts down the service. ''' #self._stop_slaves( config.getProxyServiceNodes()) #self._stop_masters(config, config.getWebServiceNodes()) self.controller.delete_nodes(self.config.serviceNodes.values()) self.config.serviceNodes = {} self.state = self.S_STOPPED @expose('GET') def get_password(self, kwargs): if len(kwargs) != 0: return HttpErrorResponse('ERROR: Arguments unexpected') return HttpJsonResponse(self.root_pass) @expose('POST') def set_password(self, kwargs): self.logger.debug('Setting password') if self.state != self.S_RUNNING: self.logger.debug('Service not runnning') return HttpErrorResponse('ERROR: Service not running') if not 'user' in kwargs: return HttpErrorResponse('ERROR: Required argument \'user\' doesn\'t exist') if not 'password' in kwargs: return HttpErrorResponse('ERROR: Required argument \'password\' doesn\'t exist') # Get the master masters = self.config.getMySQLmasters() #TODO: modify this when multiple masters try: for master in masters: client.set_password(master.ip, self.config.AGENT_PORT, kwargs['user'], kwargs['password']) except: e = sys.exc_info()[1] self.logger.exception('set_password: Could not set password: %s' % e) self.state = self.S_ERROR return HttpErrorResponse('Failed to set password') else: self.root_pass = kwargs['password'] return HttpJsonResponse() @expose('GET') def sqldump(self, kwargs): if self.state != self.S_RUNNING: return HttpErrorResponse('ERROR: Wrong state to call sqldump') master_ip = self.config.getMySQLmasters()[0].ip cmd = 'mysqldump -u mysqldb -h %s --password=%s -A' % (master_ip, self.root_pass) out, err = run_cmd(cmd) if err: return HttpErrorResponse(err) return HttpJsonResponse(out) @expose('UPLOAD') 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()