def delete(self, req, agent_id, body=None): """call buy agent""" # if force is true # will not notify agent, just delete agent from database body = body or {} force = body.get('force', False) rpc = get_client() global_data = get_global() metadata = None with global_data.delete_agent(agent_id) as agent: if not force: metadata = BaseContorller.agent_metadata(agent.agent_id) if metadata is None: raise RpcPrepareError('Can not delete offline agent, try force') agent_ipaddr = metadata.get('local_ip') secret = uuidutils.generate_uuid() # tell agent wait delete finishtime, timeout = rpcfinishtime() delete_agent_precommit = rpc.call(targetutils.target_agent(agent), ctxt={'finishtime': finishtime}, msg={'method': 'delete_agent_precommit', 'args': {'agent_id': agent.agent_id, 'agent_type': agent.agent_type, 'host': agent.host, 'agent_ipaddr': agent_ipaddr, 'secret': secret} }, timeout=timeout) if not delete_agent_precommit: raise RpcResultError('delete_agent_precommit result is None') if delete_agent_precommit.get('resultcode') != manager_common.RESULT_SUCCESS: return resultutils.results(result=delete_agent_precommit.get('result'), resultcode=manager_common.RESULT_ERROR) # if not force: # tell agent delete itself finishtime = rpcfinishtime()[0] LOG.info('Delete agent %s postcommit with secret %s' % (agent_ipaddr, secret)) rpc.cast(targetutils.target_agent(agent), ctxt={'finishtime': finishtime}, msg={'method': 'delete_agent_postcommit', 'args': {'agent_id': agent.agent_id, 'agent_type': agent.agent_type, 'host': agent.host, 'agent_ipaddr': agent_ipaddr, 'secret': secret}}) def wapper(): rpc.cast(targetutils.target_rpcserver(fanout=True), msg={'method': 'deletesource', 'args': {'agent_id': agent_id}}) threadpool.add_thread(safe_func_wrapper, wapper, LOG) result = resultutils.results(result='Delete agent success', data=[dict(agent_id=agent.agent_id, host=agent.host, status=agent.status, metadata=metadata, ports_range=jsonutils.safe_loads_as_bytes(agent.ports_range) or []) ]) return result
def _start_database(self, database, **kwargs): req = kwargs.pop('req') entity = int(database.reflection_id) _entity = entity_controller.show(req=req, entity=entity, endpoint=common.DB, body={'ports': False})['data'][0] agent_id = _entity['agent_id'] metadata = _entity['metadata'] target = targetutils.target_agent_by_string(metadata.get('agent_type'), metadata.get('host')) target.namespace = common.DB rpc = get_client() finishtime, timeout = rpcfinishtime() rpc_ret = rpc.call(target, ctxt={ 'finishtime': finishtime, 'agents': [ agent_id, ] }, msg={ 'method': 'start_entity', 'args': dict(entity=entity) }, timeout=timeout) if not rpc_ret: raise RpcResultError('create entitys result is None') if rpc_ret.get('resultcode') != manager_common.RESULT_SUCCESS: raise RpcResultError('create entity fail %s' % rpc_ret.get('result')) return rpc_ret
def notify_create(target, agent_id, entity, body, action='create'): rpc = get_client() body.setdefault('entity', entity) finishtime = body.pop('finishtime', None) if finishtime is None: finishtime, timeout = rpcfinishtime() else: timeout = finishtime - int(time.time()) + 1 if timeout < 3: raise InvalidArgument('Timeout less then 3') create_ret = rpc.call(target, ctxt={ 'finishtime': finishtime, 'agents': [ agent_id, ], 'entitys': [ entity, ] }, msg={ 'method': '%s_entity' % action, 'args': body }, timeout=timeout) return create_ret
def _revoke_database_user(self, database, auth, **kwargs): req = kwargs.pop('req') entity = int(database.reflection_id) _entity = entity_controller.show(req=req, entity=entity, endpoint=common.DB, body={'ports': False})['data'][0] agent_id = _entity['agent_id'] metadata = _entity['metadata'] if not metadata: raise InvalidArgument('Traget database agent is offline') target = targetutils.target_agent_by_string(metadata.get('agent_type'), metadata.get('host')) target.namespace = common.DB rpc = get_client() finishtime, timeout = rpcfinishtime() rpc_ret = rpc.call(target, ctxt={ 'finishtime': finishtime, 'agents': [ agent_id, ] }, msg={ 'method': 'revoke_entity', 'args': dict(entity=entity, auth=auth) }, timeout=timeout) if not rpc_ret: raise RpcResultError('revoke grant from database result is None') if rpc_ret.get('resultcode') != manager_common.RESULT_SUCCESS: raise RpcResultError('revoke grant from database fail %s' % rpc_ret.get('result')) return rpc_ret
def opentime(self, req, group_id, objtype, entity, body=None): """修改开服时间接口""" body = body or {} group_id = int(group_id) entity = int(entity) if objtype != common.GAMESERVER: raise InvalidArgument('Api just for %s' % common.GAMESERVER) opentime = int(body.pop('opentime')) if opentime < 0 or opentime >= int(time.time()) + 86400 * 15: raise InvalidArgument('opentime value error') session = endpoint_session() with session.begin(): query = model_query(session, AppEntity, filter=AppEntity.entity == entity) _entity = query.one() if _entity.objtype != objtype: raise InvalidArgument('Entity is not %s' % objtype) if _entity.group_id != group_id: raise InvalidArgument('Entity group %d not match %d' % (_entity.group_id, group_id)) metadata, ports = self._entityinfo(req=req, entity=entity) target = targetutils.target_agent_by_string(metadata.get('agent_type'), metadata.get('host')) target.namespace = common.NAME rpc = get_client() finishtime, timeout = rpcfinishtime() # with session.begin(): rpc_ret = rpc.call(target, ctxt={'finishtime': finishtime}, msg={'method': 'opentime_entity', 'args': dict(entity=entity, opentime=opentime)}, timeout=timeout) query.update({'opentime': opentime}) if not rpc_ret: raise RpcResultError('change entity opentime result is None') if rpc_ret.get('resultcode') != manager_common.RESULT_SUCCESS: raise RpcResultError('change entity opentime fail %s' % rpc_ret.get('result')) return resultutils.results(result='change entity %d opentime success' % entity)
def _unbond_database(self, session, master, slave, relation, **kwargs): req = kwargs.pop('req') entity = int(slave.reflection_id) with session.begin(subtransactions=True): _entity = entity_controller.show(req=req, entity=entity, endpoint=common.DB, body={'ports': False})['data'][0] agent_id = _entity['agent_id'] metadata = _entity['metadata'] if not metadata: raise InvalidArgument('Traget database agent is offline') target = targetutils.target_agent_by_string( metadata.get('agent_type'), metadata.get('host')) target.namespace = common.DB rpc = get_client() finishtime, timeout = rpcfinishtime() # 发送master信息到从库所在agent rpc_ret = rpc.call(target, ctxt={ 'finishtime': finishtime + 3, 'agents': [ agent_id, ] }, msg={ 'method': 'unbond_entity', 'args': dict(entity=entity, force=kwargs.get('force', False), master=dict( database_id=master.database_id, ready=relation.ready, schemas=[ schema.schema for schema in master.schemas ], )) }, timeout=timeout + 3) if not rpc_ret: raise RpcResultError('unbond database result is None') if rpc_ret.get('resultcode') != manager_common.RESULT_SUCCESS: raise RpcResultError('unbond database fail %s' % rpc_ret.get('result')) # 绑定状态设置就绪 session.delete(relation) session.flush() auth = privilegeutils.mysql_replprivileges( slave.database_id, metadata.get('local_ip')) auth['schema'] = '*' return self._revoke_database_user(master, auth, req=req)
def _slave_database(self, session, master, slave, **kwargs): req = kwargs.pop('req') with session.begin(subtransactions=True): # get slave host and port _host, _port = self._get_entity(req=req, entity=int(slave.reflection_id), raise_error=True) entity = int(master.reflection_id) _entity = entity_controller.show(req=req, entity=entity, endpoint=common.DB, body={'ports': False})['data'][0] agent_id = _entity['agent_id'] metadata = _entity['metadata'] if not metadata: raise InvalidArgument('Traget database agent is offline') target = targetutils.target_agent_by_string( metadata.get('agent_type'), metadata.get('host')) target.namespace = common.DB rpc = get_client() finishtime, timeout = rpcfinishtime() # 发送slave信息到主库所在agent rpc_ret = rpc.call(target, ctxt={ 'finishtime': finishtime + 5, 'agents': [ agent_id, ] }, msg={ 'method': 'slave_entity', 'args': dict(entity=entity, schemas=kwargs.get('schemas'), file=kwargs.get('file'), position=kwargs.get('position'), bond=dict( database_id=slave.database_id, host=_host, port=_port)) }, timeout=timeout + 5) if not rpc_ret: raise RpcResultError( 'bond slave for master database result is None') if rpc_ret.get('resultcode') != manager_common.RESULT_SUCCESS: raise RpcResultError('bond slave for master database fail %s' % rpc_ret.get('result')) return rpc_ret
def _ready_relation(self, session, master, slave, relation, **kwargs): req = kwargs.pop('req') entity = int(slave.reflection_id) with session.begin(subtransactions=True): schemas = [schema.schema for schema in master.schemas] _host, _port = self._get_entity(req, int(master.reflection_id), raise_error=True) _entity = entity_controller.show(req=req, entity=entity, endpoint=common.DB, body={'ports': False})['data'][0] agent_id = _entity['agent_id'] metadata = _entity['metadata'] if not metadata: raise InvalidArgument('Traget database agent is offline') target = targetutils.target_agent_by_string( metadata.get('agent_type'), metadata.get('host')) target.namespace = common.DB rpc = get_client() finishtime, timeout = rpcfinishtime() # 发送master信息到从库所在agent rpc_ret = rpc.call(target, ctxt={ 'finishtime': finishtime, 'agents': [ agent_id, ] }, msg={ 'method': 'entity_replication_ready', 'args': dict(entity=entity, master=dict( database_id=master.database_id, host=_host, port=_port, schemas=schemas)) }, timeout=timeout) if not rpc_ret: raise RpcResultError('get replication status result is None') if rpc_ret.get('resultcode') != manager_common.RESULT_SUCCESS: raise RpcResultError('get replication status fail %s' % rpc_ret.get('result')) # 绑定状态设置就绪 relation.ready = True return rpc_ret
def _bond_database(self, session, master, slave, relation, **kwargs): req = kwargs.pop('req') entity = int(slave.reflection_id) with session.begin(subtransactions=True): _entity = entity_controller.show(req=req, entity=entity, endpoint=common.DB, body={'ports': False})['data'][0] agent_id = _entity['agent_id'] metadata = _entity['metadata'] if not metadata: raise InvalidArgument('Traget database agent is offline') target = targetutils.target_agent_by_string( metadata.get('agent_type'), metadata.get('host')) target.namespace = common.DB rpc = get_client() finishtime, timeout = rpcfinishtime() # 发送master信息到从库所在agent rpc_ret = rpc.call(target, ctxt={ 'finishtime': finishtime + 5, 'agents': [ agent_id, ] }, msg={ 'method': 'bond_entity', 'args': dict(entity=entity, force=kwargs.get('force', False), master=dict( database_id=master.database_id, host=kwargs.get('host'), port=kwargs.get('port'), passwd=kwargs.get('passwd'), file=kwargs.get('file'), position=kwargs.get('position'), schemas=kwargs.get('schemas'))) }, timeout=timeout + 5) if not rpc_ret: raise RpcResultError('bond database result is None') if rpc_ret.get('resultcode') != manager_common.RESULT_SUCCESS: raise RpcResultError('bond database fail %s' % rpc_ret.get('result')) # 绑定状态设置就绪 relation.ready = True if not kwargs.get('schemas') else False return rpc_ret
def create_asyncrequest(body): """async request use this to create a new request argv in body request_time: unix time in seconds that client send async request finishtime: unix time in seconds that work shoud be finished after this time deadline: unix time in seconds that work will igonre after this time expire: respone expire time """ request_time = int(timeutils.realnow()) expire = body.pop('expire', 0) if expire < 0: raise InvalidArgument('Async argv expire less thne 0') try: client_request_time = int(body.pop('request_time')) except KeyError: raise InvalidArgument('Async request need argument request_time') except TypeError: raise InvalidArgument( 'request_time is not int of time or no request_time found') offset_time = request_time - client_request_time if abs(offset_time) > 5: raise InvalidArgument( 'The offset time between send and receive is %d' % offset_time) finishtime = body.pop('finishtime', None) if finishtime: finishtime = int(finishtime) + offset_time else: finishtime = rpcfinishtime(request_time)[0] if finishtime - request_time < 3: raise InvalidArgument('Job can not be finished in 3 second') deadline = body.pop('deadline', None) if deadline: deadline = int(deadline) + offset_time - 1 else: # deadline = rpcdeadline(finishtime) deadline = finishtime + 5 if deadline - finishtime < 3: raise InvalidArgument( 'Job deadline must at least 3 second after finishtime') request_id = uuidutils.generate_uuid() # req.environ[manager_common.ENV_REQUEST_ID] = request_id new_request = AsyncRequest(request_id=request_id, request_time=request_time, finishtime=finishtime, deadline=deadline, expire=expire) return new_request
def stop(self, req, group_id, objtype, entity, body=None): """ kill 强制关闭 notify 通过gm服务器通知区服关闭 """ body = body or {} kill = body.get('kill', False) notify = body.pop('notify', False) if objtype == common.GAMESERVER and notify and not kill: message = body.pop('message', '') or '' delay = body.pop('delay', 3) if delay: if not isinstance(delay, (int, long)) or delay < 3: raise InvalidArgument('Delay value error') delay = min(delay, 60) finishtime = rpcfinishtime()[0] + delay + 5 body.update({'finishtime': finishtime, 'delay': delay + 5}) url = gmurl(req, group_id, interface='closegameserver') @contextlib.contextmanager def context(reqeust_id, entitys, agents): pre_run = { 'executer': 'http', 'ekwargs': { 'url': url, 'method': 'POST', 'async': False, 'json': OrderedDict(RealSvrIds=list(entitys), Msg=message, DelayTime=delay) } } body.update({'pre_run': pre_run}) yield else: context = None body.pop('delay', None) return self._async_bluck_rpc('stop', group_id, objtype, entity, body, context)
def notify_delete(target, agent_id, entity, body, action='delete'): rpc = get_client() body.setdefault('entity', entity) token = body.get('token') finishtime = body.pop('finishtime', None) if finishtime is None: finishtime, timeout = rpcfinishtime() else: timeout = finishtime - int(time.time()) + 1 if timeout < 3: raise InvalidArgument('Timeout less then 3') if token: # send a delete token rpc.cast(target, ctxt={ 'finishtime': finishtime, 'entitys': [ entity, ] }, msg={ 'method': 'entity_token', 'args': { 'entity': entity, 'token': token } }) delete_ret = rpc.call(target, ctxt={ 'finishtime': finishtime, 'agents': [ agent_id, ], 'entitys': [ entity, ] }, msg={ 'method': '%s_entity' % action, 'args': body }, timeout=timeout) return delete_ret
def readlog(self, req, agent_id, body=None): """call by agent""" body = body or {} lines = body.get('lines', 10) rpc = get_client() metadata = BaseContorller.agent_metadata(agent_id) if metadata is None: raise RpcPrepareError('Can not get log from offline agent: %d' % agent_id) session = get_session() query = model_query(session, Agent, filter=Agent.agent_id == agent_id) agent = query.one() rpc_ret = rpc.call(targetutils.target_agent(agent), ctxt={'finishtime': rpcfinishtime()[0]}, msg={'method': 'readlog', 'args': {'lines': lines}}) if not rpc_ret: raise RpcResultError('Get log agent rpc result is None') if rpc_ret.get('resultcode') != manager_common.RESULT_SUCCESS: raise RpcResultError('Get log agent rpc result: ' + rpc_ret.get('result')) return resultutils.results(result=rpc_ret.get('result'), data=[rpc_ret.get('uri')])
def readlog(self, req, endpoint, entity, body=None): body = body or {} path = body.get('path') lines = body.get('lines', 10) if not path or '..' in path: raise InvalidArgument('Path value error') endpoint = validateutils.validate_endpoint(endpoint) entity = int(entity) session = get_session(readonly=True) query = model_query(session, AgentEntity, filter=and_(AgentEntity.endpoint == endpoint, AgentEntity.entity == entity)) _entity = query.one_or_none() if not _entity: raise InvalidArgument('no entity found for %s' % endpoint) metadata = BaseContorller.agent_metadata(_entity.agent_id) if not metadata: raise InvalidArgument('Can not get log from off line agent') target = targetutils.target_agent_by_string(manager_common.APPLICATION, metadata.get('host')) target.namespace = endpoint rpc = get_client() rpc_ret = rpc.call(target, ctxt={'finishtime': rpcfinishtime()[0]}, msg={ 'method': 'readlog', 'args': { 'entity': entity, 'path': path, 'lines': lines } }) if not rpc_ret: raise RpcResultError('Get %s.%d log rpc result is None' % (endpoint, entity)) if rpc_ret.get('resultcode') != manager_common.RESULT_SUCCESS: raise InvalidArgument(('Get %s.%d log agent rpc result: ' % (endpoint, entity)) + rpc_ret.get('result')) return resultutils.results(result=rpc_ret.get('result'), data=[rpc_ret.get('uri')])
def active(self, req, agent_id, body=None): """call buy client""" body = body or {} status = body.get('status', manager_common.ACTIVE) if status not in (manager_common.ACTIVE, manager_common.UNACTIVE): raise InvalidArgument('Argument status not right') rpc = get_client() session = get_session() query = model_query(session, Agent, filter=and_(Agent.agent_id == agent_id, Agent.status > manager_common.DELETED)) agent = query.one() # make sure agent is online metadata = BaseContorller.agent_metadata(agent.agent_id) if metadata is None: raise RpcPrepareError('Can not active or unactive a offline agent: %d' % agent_id) agent_ipaddr = metadata.get('local_ip') with session.begin(): agent.update({'status': status}) active_agent = rpc.call(targetutils.target_agent(agent), ctxt={'finishtime': rpcfinishtime()[0]}, msg={'method': 'active_agent', 'args': {'agent_id': agent_id, 'agent_ipaddr': agent_ipaddr, 'status': status} }) if not active_agent: raise RpcResultError('Active agent rpc result is None') if active_agent.pop('resultcode') != manager_common.RESULT_SUCCESS: raise RpcResultError('Call agent active or unactive fail: ' + active_agent.get('result')) result = resultutils.results(result=active_agent.pop('result'), data=[dict(agent_id=agent.agent_id, host=agent.host, agent_type=agent.agent_type, metadata=metadata, status=agent.status) ]) return result
def post_create_entity(self, entity, endpoint, **kwargs): entity = int(entity) endpoint = validateutils.validate_endpoint(endpoint) session = get_session(readonly=True) query = model_query(session, AgentEntity, filter=and_(AgentEntity.endpoint == endpoint, AgentEntity.entity == entity)) _entity = query.one() metadata = BaseContorller.agent_metadata(_entity.agent_id) if not metadata: raise RpcPrepareError('Agent not online, can not sen post create') target = targetutils.target_agent_by_string( metadata.get('agent_type'), metadata.get('host'), ) target.namespace = endpoint body = dict(entity=entity) body.update(kwargs) rpc = get_client() rpc.cast(target, ctxt={ 'finishtime': body.pop('finishtime', rpcfinishtime()[0]), 'entitys': [ entity, ] }, msg={ 'method': 'post_create_entity', 'args': body }) return resultutils.results(result='notify post create success', data=[ dict(entity=entity, agent_id=_entity.agent_id, endpoint=endpoint) ])
def reset(self, req, group_id, objtype, entity, body=None): """重置Entity程序以及配置""" body = body or {} group_id = int(group_id) entity = int(entity) # 重置程序文件,为空表示不需要重置程序文件 appfile = body.pop(common.APPFILE, None) # 重置数据库信息 databases = body.pop('databases', False) # 重置主服务器信息(gameserver专用) chiefs = body.pop('chiefs', False) # 查询entity信息 session = endpoint_session() query = model_query(session, AppEntity, filter=AppEntity.entity == entity) query = query.options(joinedload(AppEntity.databases, innerjoin=False)) _entity = query.one() if _entity.objtype != objtype: raise InvalidArgument('Entity is not %s' % objtype) if _entity.group_id != group_id: raise InvalidArgument('Entity group %d not match %d' % (_entity.group_id, group_id)) entityinfo = entity_controller.show(req=req, entity=entity, endpoint=common.NAME, body={'ports': False})['data'][0] agent_id = entityinfo['agent_id'] metadata = entityinfo['metadata'] if not metadata: raise InvalidArgument('Agent is off line, can not reset entity') # 需要更新数据库 if databases: miss = [] databases = {} # 从本地查询数据库信息 for database in _entity.databases: subtype = database.subtype schema = '%s_%s_%s_%d' % (common.NAME, objtype, subtype, entity) databases[subtype] = dict(host=database.host, port=database.port, user=database.user, passwd=database.passwd, schema=schema, character_set=database.character_set) # 必要数据库信息 NEEDED = common.DBAFFINITYS[objtype].keys() # 数据库信息不匹配,从gopdb接口反查数据库信息 if set(NEEDED) != set(databases.keys()): LOG.warning('Database not match, try find schema info from gopdb') quotes = schema_controller.quotes(req, body=dict(entitys=[entity, ], endpoint=common.NAME))['data'] for subtype in NEEDED: if subtype not in databases: # 从gopdb接口查询引用信息 schema = '%s_%s_%s_%d' % (common.NAME, objtype, subtype, entity) for quote_detail in quotes: # 确认引用不是从库且结构名称相等 if quote_detail['qdatabase_id'] == quote_detail['database_id'] \ and quote_detail['schema'] == schema: databases.setdefault(subtype, dict(host=quote_detail['host'], port=quote_detail['port'], user=quote_detail['user'], passwd=quote_detail['passwd'], schema=schema, character_set=quote_detail['character_set'])) miss.append(AreaDatabase(quote_id=quote_detail['quote_id'], database_id=quote_detail['qdatabase_id'], entity=entity, subtype=subtype, host=quote_detail['host'], port=quote_detail['port'], user=quote_detail['user'], passwd=quote_detail['passwd'], ro_user=quote_detail['ro_user'], ro_passwd=quote_detail['ro_passwd'], character_set=quote_detail['character_set']) ) quotes.remove(quote_detail) break if subtype not in databases: LOG.critical('Miss database of %s' % schema) # 数据库信息无法从gopdb中反查到 raise ValueError('Not %s.%s database found for %d' % (objtype, subtype, entity)) self._validate_databases(objtype, databases) # 有数据库信息遗漏 if miss: with session.begin(): for obj in miss: session.add(obj) session.flush() if objtype == common.GAMESERVER and chiefs: chiefs = {} cross_id = _entity.cross_id if cross_id is None: raise ValueError('%s.%d cross_id is None' % (objtype, entity)) query = model_query(session, AppEntity, filter=and_(AppEntity.group_id == group_id, or_(AppEntity.entity == cross_id, AppEntity.objtype == common.GMSERVER))) _chiefs = query.all() if len(_chiefs) != 2: raise ValueError('Try find %s.%d chiefs from local database error' % (objtype, entity)) for chief in _chiefs: for _objtype in (common.GMSERVER, common.CROSSSERVER): _metadata, ports = self._entityinfo(req, chief.entity) if not _metadata: raise InvalidArgument('Metadata of %s.%d is none' % (_objtype, chief.entity)) if chief.objtype == _objtype: chiefs[_objtype] = dict(entity=chief.entity, ports=ports, local_ip=_metadata.get('local_ip')) if len(chiefs) != 2: raise ValueError('%s.%d chiefs error' % (objtype, entity)) target = targetutils.target_agent_by_string(metadata.get('agent_type'), metadata.get('host')) target.namespace = common.NAME rpc = get_client() finishtime, timeout = rpcfinishtime() if appfile: finishtime += 30 timeout += 35 rpc_ret = rpc.call(target, ctxt={'finishtime': finishtime, 'agents': [agent_id, ]}, msg={'method': 'reset_entity', 'args': dict(entity=entity, appfile=appfile, opentime=_entity.opentime, databases=databases, chiefs=chiefs)}, timeout=timeout) if not rpc_ret: raise RpcResultError('reset entity result is None') if rpc_ret.get('resultcode') != manager_common.RESULT_SUCCESS: raise RpcResultError('reset entity fail %s' % rpc_ret.get('result')) return resultutils.results(result='reset entity %d success' % entity)
def update(self, req, group_id, objtype, entity, body=None): body = body or {} group_id = int(group_id) entity = int(entity) status = body.get('status', common.OK) if status not in (common.UNACTIVE, common.OK): raise InvalidArgument('Status not in 0, 1, 2') session = endpoint_session() glock = get_gamelock() query = model_query(session, AppEntity, filter=AppEntity.entity == entity) if objtype == common.GAMESERVER: query = query.options(joinedload(AppEntity.areas, innerjoin=False)) _entity = query.one() if status == _entity.status: return resultutils.results(result='%s entity status in same' % objtype) if _entity.status not in (common.OK, common.UNACTIVE): return resultutils.results( resultcode=manager_common.RESULT_ERROR, result='%s entity is not ok or unactive' % objtype) if _entity.objtype != objtype: raise InvalidArgument('Objtype not match') if _entity.group_id != group_id: raise InvalidArgument('Group id not match') entityinfo = entity_controller.show(req=req, entity=entity, endpoint=common.NAME, body={'ports': False})['data'][0] agent_id = entityinfo['agent_id'] metadata = entityinfo['metadata'] if not metadata: raise InvalidArgument('Agent is off line, can not reset entity') rpc = get_client() target = targetutils.target_agent_by_string(metadata.get('agent_type'), metadata.get('host')) target.namespace = common.NAME if objtype == common.GAMESERVER: lock = functools.partial( glock.arealock, group=group_id, areas=[area.area_id for area in _entity.areas]) else: lock = functools.partial(glock.grouplock, group=group_id) with lock(): with session.begin(): _entity.status = status session.flush() finishtime, timeout = rpcfinishtime() rpc_ret = rpc.call(target, ctxt={ 'finishtime': finishtime, 'agents': [ agent_id, ] }, msg={ 'method': 'change_status', 'args': dict(entity=entity, status=status) }, timeout=timeout) if not rpc_ret: raise RpcResultError('change entity sttus result is None') if rpc_ret.get('resultcode') != manager_common.RESULT_SUCCESS: raise RpcResultError('change entity status fail %s' % rpc_ret.get('result')) return resultutils.results(result='%s entity update success' % objtype)
def create(self, req, group_id, objtype, body=None): body = body or {} group_id = int(group_id) jsonutils.schema_validate(body, self.CREATEAPPENTITY) if body.get('packages'): raise InvalidArgument('Package parameter is removed') # 找cross服务, gameserver专用 cross_id = body.pop('cross_id', None) # 开服时间, gameserver专用 opentime = body.pop('opentime', None) # 区服显示id, gameserver专用 show_id = body.pop('show_id', None) # 区服显示民称, gameserver专用 areaname = body.pop('areaname', None) # 平台类型 platform = body.get('platform') include = set(body.pop('include', [])) exclude = set(body.pop('exclude', [])) if include and exclude: raise InvalidArgument('Both packages and exclude is forbidden') packages = [] session = endpoint_session() if objtype == common.GAMESERVER: platform = common.PlatformTypeMap.get(platform) if not areaname or not opentime or not platform or not show_id: raise InvalidArgument( '%s need opentime and areaname and platform and show_id' % objtype) # 安装文件信息 appfile = body.pop(common.APPFILE) LOG.debug('Try find agent and database for entity') # 选择实例运行服务器 agent_id = body.get('agent_id') or self._agentselect( req, objtype, **body) # 选择实例运行数据库 databases = self._dbselect(req, objtype, **body) # 校验数据库信息 if not self._validate_databases(objtype, databases): raise InvalidArgument('Miss some database') LOG.info( 'Find agent and database for entity success, to agent %d, to databse %s' % (agent_id, str(databases))) query = model_query(session, Group, filter=Group.group_id == group_id) joins = joinedload(Group.entitys, innerjoin=False) joins = joins.joinedload(AppEntity.databases, innerjoin=False) query = query.options(joins) _group = query.one() glock = get_gamelock() with glock.grouplock(group_id): if objtype == common.GAMESERVER: _pquery = model_query(session, Package, filter=Package.group_id == group_id) _packages = set([ p.package_id for p in _pquery.all() if p.platform & platform ]) if (include - _packages) or (exclude - _packages): raise InvalidArgument( 'Package can not be found in include or exclude') if exclude: packages = _packages - exclude elif include: packages = include else: packages = _packages typemap = {} for _entity in _group.entitys: # 跳过未激活的实体 if _entity.status != common.OK: continue try: typemap[_entity.objtype].append(_entity) except KeyError: typemap[_entity.objtype] = [ _entity, ] # 前置实体 chiefs = None # 相同类型的实例列表 same_type_entitys = typemap.get(objtype, []) if objtype == common.GMSERVER: # GM服务不允许相同实例,必须clean掉所有同组GM服务器 for _entity in _group.entitys: if _entity.objtype == common.GMSERVER: return resultutils.results( result='create entity fail, %s duplicate in group' % objtype, resultcode=manager_common.RESULT_ERROR) else: # 非gm实体添加需要先找到同组的gm try: gm = typemap[common.GMSERVER][0] if gm.status <= common.DELETED: return resultutils.results( result='Create entity fail, gm mark deleted', resultcode=manager_common.RESULT_ERROR) except KeyError as e: return resultutils.results( result='Create entity fail, can not find GMSERVER: %s' % e.message, resultcode=manager_common.RESULT_ERROR) if objtype == common.GAMESERVER: if model_count_with_key( session, GameArea, filter=and_(GameArea.group_id == group_id, GameArea.areaname == areaname)): return resultutils.results( result='Create entity fail, name exist', resultcode=manager_common.RESULT_ERROR) cross = None # 游戏服务器需要在同组中找到cross实例 try: crossservers = typemap[common.CROSSSERVER] except KeyError as e: return resultutils.results( result= 'create entity fail, can not find my chief: %s' % e.message, resultcode=manager_common.RESULT_ERROR) # 如果指定了cross实例id if cross_id: # 判断cross实例id是否在当前组中 for _cross in crossservers: if cross_id == _cross.entity: cross = _cross break else: # 游戏服没有相同实例,直接使用第一个cross实例 if not same_type_entitys: cross = crossservers[0] else: # 统计所有cross实例的引用次数 counted = set() counter = dict() for _cross in crossservers: counter.setdefault(_cross.entity, 0) # 查询当前组内所有entity对应的cross_id for _entity in _group.entitys: if _entity.objtype != common.GAMESERVER: continue if _entity.cross_id in counted: continue counter[_entity.cross_id] += 1 # 选取引用次数最少的cross_id cross_id = sorted( zip(counter.itervalues(), counter.iterkeys()))[0][1] for _cross in crossservers: if cross_id == _cross.entity: cross = _cross break if not cross: raise InvalidArgument( 'cross server can not be found or not active') # 获取实体相关服务器信息(端口/ip) maps = entity_controller.shows( endpoint=common.NAME, entitys=[gm.entity, cross.entity]) for v in six.itervalues(maps): if v is None: raise InvalidArgument( 'Get chiefs info error, agent not online?') chiefs = dict() # 战场与GM服务器信息 for chief in (cross, gm): chiefmetadata = maps.get(chief.entity).get('metadata') ports = maps.get(chief.entity).get('ports') if not chiefmetadata: raise InvalidArgument( '%s.%d is offline' % (chief.objtype, chief.entity)) need = common.POSTS_COUNT[chief.objtype] if need and len(ports) != need: raise InvalidArgument('%s.%d port count error, ' 'find %d, need %d' % (chief.objtype, chief.entity, len(ports), need)) chiefs.setdefault( chief.objtype, dict(entity=chief.entity, ports=ports, local_ip=chiefmetadata.get('local_ip'))) cross_id = cross.entity # 完整的rpc数据包 create_body = dict(objtype=objtype, appfile=appfile, databases=databases, chiefs=chiefs, entity=int(body.get('entity', 0))) with session.begin(): body.setdefault('finishtime', rpcfinishtime()[0] + 5) try: create_result = entity_controller.create( req=req, agent_id=agent_id, endpoint=common.NAME, body=create_body)['data'][0] except RpcResultError as e: LOG.error('Create entity rpc call fail: %s' % e.message) raise InvalidArgument(e.message) entity = create_result.get('entity') rpc_result = create_result.get('notify') LOG.info('Create new entity %d' % entity) LOG.debug('Entity controller create rpc result %s', str(rpc_result)) # 插入实体信息 appentity = AppEntity(entity=entity, agent_id=agent_id, group_id=group_id, objtype=objtype, cross_id=cross_id, opentime=opentime, platform=platform) session.add(appentity) session.flush() if objtype == common.GAMESERVER: areaname = areaname.decode('utf-8') if isinstance( areaname, six.binary_type) else areaname gamearea = GameArea(group_id=_group.group_id, show_id=show_id, areaname=areaname, gid=None, entity=appentity.entity) session.add(gamearea) session.flush() # area id插入渠道包包含列表中,批量操作 if packages: for package_id in packages: session.add( PackageArea(package_id=package_id, area_id=gamearea.area_id)) session.flush() # 插入数据库绑定信息 if rpc_result.get('databases'): self._bondto(session, entity, rpc_result.get('databases')) else: LOG.error('New entity database miss') _result = dict(entity=entity, objtype=objtype, agent_id=agent_id, connection=rpc_result.get('connection'), ports=rpc_result.get('ports'), databases=rpc_result.get('databases')) areas = [] if objtype == common.GAMESERVER: areas = [ dict(area_id=gamearea.area_id, gid=0, areaname=areaname, show_id=show_id) ] _result.setdefault('areas', areas) _result.setdefault('cross_id', cross_id) _result.setdefault('opentime', opentime) _result.setdefault('platform', platform) _result.setdefault('packages', sorted(packages)) # 添加端口 # threadpool.add_thread(port_controller.unsafe_create, # agent_id, common.NAME, entity, rpc_result.get('ports')) port_controller.unsafe_create(agent_id, common.NAME, entity, rpc_result.get('ports')) # agent 后续通知 threadpool.add_thread(entity_controller.post_create_entity, entity, common.NAME, objtype=objtype, status=common.UNACTIVE, opentime=opentime, group_id=group_id, areas=areas) return resultutils.results(result='create %s entity success' % objtype, data=[ _result, ])
def clean(self, req, group_id, objtype, entity, body=None): """彻底删除entity""" body = body or {} action = body.pop('clean', 'unquote') force = False ignores = body.pop('ignores', []) if action not in ('delete', 'unquote', 'force'): raise InvalidArgument('clean option value error') if action == 'force': action = 'delete' force = True group_id = int(group_id) entity = int(entity) session = endpoint_session() glock = get_gamelock() metadata, ports = self._entityinfo(req=req, entity=entity) if not metadata: raise InvalidArgument('Agent offline, can not delete entity') query = model_query(session, AppEntity, filter=AppEntity.entity == entity) query = query.options(joinedload(AppEntity.databases, innerjoin=False)) _entity = query.one() rollbacks = [] def _rollback(): for back in rollbacks: __database_id = back.get('database_id') __schema = back.get('schema') __quote_id = back.get('quote_id') rbody = dict(quote_id=__quote_id, entity=entity) rbody.setdefault(dbcommon.ENDPOINTKEY, common.NAME) try: schema_controller.bond(req, database_id=__database_id, schema=__schema, body=rbody) except Exception: LOG.error('rollback entity %d quote %d.%s.%d fail' % (entity, __database_id, schema, __quote_id)) with glock.grouplock(group=group_id): target = targetutils.target_agent_by_string(metadata.get('agent_type'), metadata.get('host')) target.namespace = common.NAME rpc = get_client() finishtime, timeout = rpcfinishtime() LOG.warning('Clean entity %s.%d with action %s' % (objtype, entity, action)) with session.begin(): rpc_ret = rpc.call(target, ctxt={'finishtime': finishtime}, msg={'method': 'stoped', 'args': dict(entity=entity)}) if not rpc_ret: raise RpcResultError('check entity is stoped result is None') if rpc_ret.get('resultcode') != manager_common.RESULT_SUCCESS: raise RpcResultError('check entity is stoped fail, running') with session.begin(): if _entity.status != common.DELETED: raise InvalidArgument('Entity status is not DELETED, ' 'mark status to DELETED before delete it') if _entity.objtype != objtype: raise InvalidArgument('Objtype not match') if _entity.group_id != group_id: raise InvalidArgument('Group id not match') # esure database delete if action == 'delete': LOG.warning('Clean option is delete, can not rollback when fail') if not force: for _database in _entity.databases: schema = '%s_%s_%s_%d' % (common.NAME, objtype, _database.subtype, entity) schema_info = schema_controller.show(req=req, database_id=_database.database_id, schema=schema, body={'quotes': True})['data'][0] quotes = {} for _quote in schema_info['quotes']: quotes[_quote.get('quote_id')] = _quote.get('desc') if _database.quote_id not in quotes.keys(): # if set(quotes) != set([_database.quote_id]): result = 'delete %s:%d fail' % (objtype, entity) reason = ': database [%d].%s quote: %s' % (_database.database_id, schema, str(quotes)) return resultutils.results(result=(result + reason), resultcode=manager_common.RESULT_ERROR) quotes.pop(_database.quote_id) for quote_id in quotes.keys(): if quotes[quote_id] in ignores: quotes.pop(quote_id, None) if quotes: if LOG.isEnabledFor(logging.DEBUG): LOG.debug('quotes not match for %d: %s' % (schema_info['schema_id'], schema)) for quote_id in quotes.keys(): LOG.debug('quote %d: %s exist' % (quote_id, quotes[quote_id])) LOG.debug('Can not delete schema before delete quotes') return resultutils.results(result='Quotes not match', resultcode=manager_common.RESULT_ERROR) LOG.info('Databae quotes check success for %s' % schema) # clean database for _database in _entity.databases: schema = '%s_%s_%s_%d' % (common.NAME, objtype, _database.subtype, entity) if action == 'delete': LOG.warning('Delete schema %s from %d' % (schema, _database.database_id)) try: schema_controller.delete(req=req, database_id=_database.database_id, schema=schema, body={'unquotes': [_database.quote_id], 'ignores': ignores, 'force': force}) except GopdbError as e: LOG.error('Delete schema:%s from %d fail, %s' % (schema, _database.database_id, e.message)) if not force: raise e except Exception: LOG.exception('Delete schema:%s from %d fail' % (schema, _database.database_id)) if not force: raise elif action == 'unquote': LOG.info('Try unquote %d' % _database.quote_id) try: quote = schema_controller.unquote(req=req, quote_id=_database.quote_id)['data'][0] if quote.get('database_id') != _database.database_id: LOG.critical('quote %d with database %d, not %d' % (_database.quote_id, quote.get('database_id'), _database.database_id)) raise RuntimeError('Data error, quote database not the same') rollbacks.append(dict(database_id=_database.database_id, quote_id=_database.quote_id, schema=schema)) except Exception as e: LOG.error('Unquote %d fail, try rollback' % _database.quote_id) if not force: threadpool.add_thread(_rollback) raise e token = uuidutils.generate_uuid() LOG.info('Send delete command with token %s' % token) session.delete(_entity) session.flush() try: entity_controller.delete(req, common.NAME, entity=entity, body=dict(token=token)) except Exception as e: # roll back unquote threadpool.add_thread(_rollback) raise e return resultutils.results(result='delete %s:%d success' % (objtype, entity), data=[dict(entity=entity, objtype=objtype, ports=ports, metadata=metadata)])
def area(self, req, group_id, body=None): """change entity area""" body = body or {} try: group_id = int(group_id) except (TypeError, ValueError): raise InvalidArgument('Group id value error') area_id = body.get('area_id') areaname = body.get('areaname') show_id = body.get('show_id') if not areaname and not show_id: raise InvalidArgument('No value change') rpc = get_client() session = endpoint_session() query = model_query(session, GameArea, filter=GameArea.area_id == area_id) with session.begin(): area = query.one_or_none() if not area: raise InvalidArgument('No area found') if area.group_id != group_id: raise InvalidArgument('Area group not %d' % group_id) entityinfo = entity_controller.show(req=req, entity=area.entity, endpoint=common.NAME, body={'ports': False})['data'][0] agent_id = entityinfo['agent_id'] metadata = entityinfo['metadata'] if not metadata: raise InvalidArgument( 'Agent is off line, can not reset entity') if areaname: if model_count_with_key(session, GameArea, filter=and_( GameArea.group_id == group_id, GameArea.areaname == areaname)): raise InvalidArgument('Area name duplicate in group %d' % group_id) area.areaname = areaname if show_id: area.show_id = show_id target = targetutils.target_agent_by_string( metadata.get('agent_type'), metadata.get('host')) target.namespace = common.NAME finishtime, timeout = rpcfinishtime() rpc_ret = rpc.call(target, ctxt={ 'finishtime': finishtime, 'agents': [ agent_id, ] }, msg={ 'method': 'change_entity_area', 'args': dict(entity=area.entity, area_id=area.area_id, show_id=area.show_id, areaname=area.areaname) }, timeout=timeout) if not rpc_ret: raise RpcResultError('change entity area result is None') if rpc_ret.get('resultcode') != manager_common.RESULT_SUCCESS: raise RpcResultError('change entity area fail %s' % rpc_ret.get('result')) session.flush() return resultutils.results(result='change group areas success')
def merge(self, req, body=None): """合服接口,用于合服, 部分代码和create代码一直,未整合""" body = body or {} jsonutils.schema_validate(body, self.MERGEAPPENTITYS) group_id = body.pop('group_id') # 需要合并的实体 entitys = list(set(body.pop('entitys'))) entitys.sort() session = endpoint_session() # 安装文件信息 appfile = body.pop(common.APPFILE) # 选择合并后实例运行服务器 agent_id = body.get('agent_id') or self._agentselect( req, common.GAMESERVER, **body) # 选择合并后实体数据库 databases = self._dbselect(req, common.GAMESERVER, **body) opentime = body.get('opentime') # 合服任务ID uuid = uuidutils.generate_uuid() # chiefs信息初始化 query = model_query(session, AppEntity, filter=and_( AppEntity.group_id == group_id, AppEntity.objtype.in_( [common.GMSERVER, common.CROSSSERVER]))) # 找到同组的gm和战场服 gm = None cross = None crosss = [] # 默认平台识标 platform = None # 锁组 glock = get_gamelock() with glock.grouplock(group_id): if model_count_with_key(session, MergeEntity, filter=MergeEntity.entity.in_(entitys)): raise InvalidArgument('Target entity merged or in mergeing') for appentity in query: if appentity.status != common.OK: continue if appentity.objtype == common.GMSERVER: gm = appentity else: crosss.append(appentity) if not gm: raise InvalidArgument( 'Group not exist or gm not active/exist?') if not crosss: raise InvalidArgument('Group has no cross server?') if not body.get('cross_id'): cross = crosss[0] else: for appentity in crosss: if appentity.entity == body.get('cross_id'): cross = appentity break if not cross: raise InvalidArgument('cross server can not be found?') # 获取实体相关服务器信息(端口/ip) maps = entity_controller.shows(endpoint=common.NAME, entitys=[gm.entity, cross.entity]) chiefs = dict() # 战场与GM服务器信息 for chief in (cross, gm): chiefmetadata = maps.get(chief.entity).get('metadata') ports = maps.get(chief.entity).get('ports') if not chiefmetadata: raise InvalidArgument('%s.%d is offline' % (chief.objtype, chief.entity)) need = common.POSTS_COUNT[chief.objtype] if need and len(ports) != need: raise InvalidArgument( '%s.%d port count error, ' 'find %d, need %d' % (chief.objtype, chief.entity, len(ports), need)) chiefs.setdefault( chief.objtype, dict(entity=chief.entity, ports=ports, local_ip=chiefmetadata.get('local_ip'))) # 需要合服的实体 appentitys = [] query = model_query(session, AppEntity, filter=and_(AppEntity.group_id == group_id, AppEntity.entity.in_(entitys))) query = query.options(joinedload(AppEntity.areas, innerjoin=False)) with session.begin(): for appentity in query: if appentity.objtype != common.GAMESERVER: raise InvalidArgument( 'Target entity %d is not %s' % (appentity.entity, common.GAMESERVER)) if appentity.status != common.UNACTIVE: raise InvalidArgument( 'Target entity %d is not unactive' % appentity.entity) if not appentity.areas: raise InvalidArgument('Target entity %d has no area?' % appentity.entity) if appentity.versions: raise InvalidArgument( 'Traget entity %d version is not None' % appentity.entity) if platform is None: platform = appentity.platform else: # 区服平台不相同, 位操作合并platform platform = platform | appentity.platform appentitys.append(appentity) if not opentime: opentime = appentity.opentime if len(appentitys) != len(entitys): raise InvalidArgument('Can not match entitys count') # 完整的rpc数据包,准备发送合服命令到agent body = dict(appfile=appfile, databases=databases, opentime=opentime, chiefs=chiefs, uuid=uuid, entitys=entitys) body.setdefault('finishtime', rpcfinishtime()[0] + 5) try: create_result = entity_controller.create( req=req, agent_id=agent_id, endpoint=common.NAME, body=body, action='merge')['data'][0] except RpcResultError as e: LOG.error('Create entity rpc call fail: %s' % e.message) raise InvalidArgument(e.message) mergetd_entity = create_result.get('entity') rpc_result = create_result.get('notify') LOG.info('Merge to entity %d, agent %d' % (mergetd_entity, agent_id)) LOG.debug('Entity controller merge rpc result %s' % str(rpc_result)) # 插入实体信息 appentity = AppEntity(entity=mergetd_entity, agent_id=agent_id, group_id=group_id, objtype=common.GAMESERVER, cross_id=cross.entity, opentime=opentime, platform=platform) session.add(appentity) session.flush() # 插入数据库绑定信息 if rpc_result.get('databases'): self._bondto(session, mergetd_entity, rpc_result.get('databases')) else: LOG.error('New entity database miss') # 插入合服记录 mtask = MergeTask(uuid=uuid, entity=mergetd_entity, mergetime=int(time.time())) session.add(mtask) session.flush() for _appentity in appentitys: session.add( MergeEntity(entity=_appentity.entity, uuid=uuid)) session.flush() # 批量修改被合并服的状态 query.update({'status': common.MERGEING}, synchronize_session=False) session.flush() port_controller.unsafe_create(agent_id, common.NAME, mergetd_entity, rpc_result.get('ports')) # agent 后续通知 threadpool.add_thread(entity_controller.post_create_entity, appentity.entity, common.NAME, objtype=common.GAMESERVER, status=common.UNACTIVE, opentime=opentime, group_id=group_id, areas=[]) # 添加端口 # threadpool.add_thread(port_controller.unsafe_create, # agent_id, common.NAME, mergetd_entity, rpc_result.get('ports')) return resultutils.results( result='entitys is mergeing', data=[dict(uuid=uuid, entitys=entitys, entity=mergetd_entity)])