def get_service_status_without_restart(self, ctx, service): """ :param service: instance of type "Service" (module_name - the name of the service module, case-insensitive version - specify the service version, which can be either: (1) full git commit hash of the module version (2) semantic version or semantic version specification Note: semantic version lookup will only work for released versions of the module. (3) release tag, which is one of: dev | beta | release This information is always fetched from the Catalog, so for more details on specifying the version, see the Catalog documentation for the get_module_version method.) -> structure: parameter "module_name" of String, parameter "version" of String :returns: instance of type "ServiceStatus" (module_name - name of the service module version - semantic version number of the service module git_commit_hash - git commit hash of the service module release_tags - list of release tags currently for this service module (dev/beta/release) url - the url of the service up - 1 if the service is up, 0 otherwise status - status of the service as reported by rancher health - health of the service as reported by Rancher TODO: add something to return: string last_request_timestamp;) -> structure: parameter "module_name" of String, parameter "version" of String, parameter "git_commit_hash" of String, parameter "release_tags" of list of String, parameter "hash" of String, parameter "url" of String, parameter "up" of type "boolean", parameter "status" of String, parameter "health" of String """ # ctx is the context object # return variables are: status #BEGIN get_service_status_without_restart cc = Catalog(self.CATALOG_URL, token=ctx['token']) mv = cc.get_module_version({ 'module_name': service['module_name'], 'version': service['version'] }) if 'dynamic_service' not in mv: raise ValueError( 'Specified module is not marked as a dynamic service. (' + mv['module_name'] + '-' + mv['git_commit_hash'] + ')') if mv['dynamic_service'] != 1: raise ValueError( 'Specified module is not marked as a dynamic service. (' + mv['module_name'] + '-' + mv['git_commit_hash'] + ')') status = self.get_single_service_status(mv) #END get_service_status_without_restart # At some point might do deeper type checking... if not isinstance(status, dict): raise ValueError( 'Method get_service_status_without_restart return value ' + 'status is not type dict as required.') # return the results return [status]
def create_stack(self, service): cc = Catalog(self.CATALOG_URL, token=self.CATALOG_ADMIN_TOKEN) param = { 'module_name': service['module_name'], 'version': service['version'] } mv = cc.get_module_version(param) if 'dynamic_service' not in mv: raise ValueError( 'Specified module is not marked as a dynamic service. (' + mv['module_name'] + '-' + mv['git_commit_hash'] + ')') if mv['dynamic_service'] != 1: raise ValueError( 'Specified module is not marked as a dynamic service. (' + mv['module_name'] + '-' + mv['git_commit_hash'] + ')') secure_param_list = cc.get_secure_config_params({ 'module_name': mv['module_name'], 'version': mv['git_commit_hash'] }) # Construct the docker compose and rancher compose file param['client_group'] = 'service' param['function_name'] = 'service' mounts = [] mounts_list = cc.list_volume_mounts(param) if len(mounts_list) > 0: mounts = mounts_list[0]['volume_mounts'] docker_compose, rancher_compose, is_new_stack = self.create_compose_files( mv, secure_param_list, mounts) # To do: try to use API to send docker-compose directly instead of needing to write to disk ymlpath = self.SCRATCH_DIR + '/' + mv['module_name'] + '/' + str( int(time.time() * 1000)) os.makedirs(ymlpath) docker_compose_path = ymlpath + '/docker-compose.yml' rancher_compose_path = ymlpath + '/rancher-compose.yml' with open(docker_compose_path, 'w') as outfile: outfile.write( yaml.safe_dump(docker_compose, default_flow_style=False)) # can be extended later with open(rancher_compose_path, 'w') as outfile: outfile.write( yaml.safe_dump(rancher_compose, default_flow_style=False)) return mv, is_new_stack, ymlpath
class CatalogCache(object): def __init__(self, config): self.catalog_url = config.get('catalog-service-url') self.catalog = Catalog(self.catalog_url, token=config['admin_token']) self.module_cache = dict() def get_volume_mounts(self, module, method, cgroup): req = { 'module_name': module, 'function_name': method, 'client_group': cgroup } resp = self.catalog.list_volume_mounts(req) if len(resp) > 0: return resp[0]['volume_mounts'] else: return [] def get_module_info(self, module, version): # Look up the module info if module not in self.module_cache: req = {'module_name': module} if version is not None: req['version'] = version # Get the image version from the catalog and cache it module_info = self.catalog.get_module_version(req) # Lookup secure params req['load_all_versions'] = 0 sp = self.catalog.get_secure_config_params(req) module_info['secure_config_params'] = sp module_info['cached'] = False self.module_cache[module] = module_info else: # Use the cache module_info = self.module_cache[module] module_info['cached'] = True return module_info
def __init__(self, config): self.catalog_url = config.get('catalog-service-url') self.catalog = Catalog(self.catalog_url, token=config['admin_token']) self.module_cache = dict()
def get_service_log_web_socket(self, ctx, params): """ returns connection info for a websocket connection to get realtime service logs :param params: instance of type "GetServiceLogParams" (optional instance_id to get logs for a specific instance. Otherwise logs from all instances are returned, TODO: add line number constraints.) -> structure: parameter "service" of type "Service" (module_name - the name of the service module, case-insensitive version - specify the service version, which can be either: (1) full git commit hash of the module version (2) semantic version or semantic version specification Note: semantic version lookup will only work for released versions of the module. (3) release tag, which is one of: dev | beta | release This information is always fetched from the Catalog, so for more details on specifying the version, see the Catalog documentation for the get_module_version method.) -> structure: parameter "module_name" of String, parameter "version" of String, parameter "instance_id" of String :returns: instance of list of type "ServiceLogWebSocket" -> structure: parameter "instance_id" of String, parameter "socket_url" of String """ # ctx is the context object # return variables are: sockets #BEGIN get_service_log_web_socket service = params['service'] user_id = ctx['user_id'] cc = Catalog(self.CATALOG_URL, token=ctx['token']) module = cc.get_module_info({'module_name': service['module_name']}) has_access = False for o in module['owners']: if o == user_id: has_access = True if not has_access: if cc.is_admin(user_id) == 1: has_access = True if not has_access: raise ValueError( 'Only module owners and catalog admins can view service logs.') mv = cc.get_module_version({ 'module_name': service['module_name'], 'version': service['version'] }) if 'dynamic_service' not in mv: raise ValueError( 'Specified module is not marked as a dynamic service. (' + mv['module_name'] + '-' + mv['git_commit_hash'] + ')') if mv['dynamic_service'] != 1: raise ValueError( 'Specified module is not marked as a dynamic service. (' + mv['module_name'] + '-' + mv['git_commit_hash'] + ')') rancher = gdapi.Client(url=self.RANCHER_URL, access_key=self.RANCHER_ACCESS_KEY, secret_key=self.RANCHER_SECRET_KEY) #service_info = rancher.list_servicess(name=self.get_service_name(mv)) GET_SERVICE_URL = self.RANCHER_URL + '/v1/services?name=' + self.get_service_name( mv) + '&include=instances' service_info = requests.get(GET_SERVICE_URL, auth=(self.RANCHER_ACCESS_KEY, self.RANCHER_SECRET_KEY), verify=False).json() if len(service_info['data']) == 0: raise ValueError( 'Unable to fetch service information. That service version may not be available.' ) if len(service_info['data'][0]['instances']) == 0: raise ValueError( 'The service version specified has no available container instances.' ) instances = service_info['data'][0]['instances'] #pprint(instances) match_instance_id = False if 'instance_id' in params: match_instance_id = True sockets = [] for i in instances: if match_instance_id and params['instance_id'] != i['id']: continue LOG_URL = i['actions']['logs'] payload = {'follow': True} log_ws = requests.post(LOG_URL, data=json.dumps(payload), auth=(self.RANCHER_ACCESS_KEY, self.RANCHER_SECRET_KEY), verify=False).json() sockets.append({ 'instance_id': i['id'], 'socket_url': log_ws['url'] + '?token=' + log_ws['token'] }) #END get_service_log_web_socket # At some point might do deeper type checking... if not isinstance(sockets, list): raise ValueError( 'Method get_service_log_web_socket return value ' + 'sockets is not type list as required.') # return the results return [sockets]
def get_service_status(self, ctx, service): """ For a given service, check on the status. If the service is down or not running, this function will attempt to start or restart the service once, then return the status. This function will throw an error if the specified service cannot be found or encountered errors on startup. :param service: instance of type "Service" (module_name - the name of the service module, case-insensitive version - specify the service version, which can be either: (1) full git commit hash of the module version (2) semantic version or semantic version specification Note: semantic version lookup will only work for released versions of the module. (3) release tag, which is one of: dev | beta | release This information is always fetched from the Catalog, so for more details on specifying the version, see the Catalog documentation for the get_module_version method.) -> structure: parameter "module_name" of String, parameter "version" of String :returns: instance of type "ServiceStatus" (module_name - name of the service module version - semantic version number of the service module git_commit_hash - git commit hash of the service module release_tags - list of release tags currently for this service module (dev/beta/release) url - the url of the service up - 1 if the service is up, 0 otherwise status - status of the service as reported by rancher health - health of the service as reported by Rancher TODO: add something to return: string last_request_timestamp;) -> structure: parameter "module_name" of String, parameter "version" of String, parameter "git_commit_hash" of String, parameter "release_tags" of list of String, parameter "hash" of String, parameter "url" of String, parameter "up" of type "boolean", parameter "status" of String, parameter "health" of String """ # ctx is the context object # return variables are: status #BEGIN get_service_status # TODO: handle case where version is not registered in the catalog- this may be the case for core services # that were not registered in the usual way. # first get infor from the catalog- it must be a dynamic service cc = Catalog(self.CATALOG_URL, token=ctx['token']) mv = cc.get_module_version({ 'module_name': service['module_name'], 'version': service['version'] }) if 'dynamic_service' not in mv: raise ValueError( 'Specified module is not marked as a dynamic service. (' + mv['module_name'] + '-' + mv['git_commit_hash'] + ')') if mv['dynamic_service'] != 1: raise ValueError( 'Specified module is not marked as a dynamic service. (' + mv['module_name'] + '-' + mv['git_commit_hash'] + ')') status = self.get_single_service_status(mv) # if we cannot get the status, or it is not up, then try to start it if status is None or status['up'] != 1: self.start(ctx, service) # try to get status status = self.get_single_service_status(mv) if status is None: raise ValueError( 'Unable to get service status, or service was unable to start properly' ) #END get_service_status # At some point might do deeper type checking... if not isinstance(status, dict): raise ValueError('Method get_service_status return value ' + 'status is not type dict as required.') # return the results return [status]
def list_service_status(self, ctx, params): """ :param params: instance of type "ListServiceStatusParams" (not yet implemented funcdef pause(Service service) returns (ServiceStatus status);) -> structure: parameter "is_up" of type "boolean", parameter "module_names" of list of String :returns: instance of list of type "ServiceStatus" (module_name - name of the service module version - semantic version number of the service module git_commit_hash - git commit hash of the service module release_tags - list of release tags currently for this service module (dev/beta/release) url - the url of the service up - 1 if the service is up, 0 otherwise status - status of the service as reported by rancher health - health of the service as reported by Rancher TODO: add something to return: string last_request_timestamp;) -> structure: parameter "module_name" of String, parameter "version" of String, parameter "git_commit_hash" of String, parameter "release_tags" of list of String, parameter "hash" of String, parameter "url" of String, parameter "up" of type "boolean", parameter "status" of String, parameter "health" of String """ # ctx is the context object # return variables are: returnVal #BEGIN list_service_status rancher = gdapi.Client(url=self.RANCHER_URL, access_key=self.RANCHER_ACCESS_KEY, secret_key=self.RANCHER_SECRET_KEY) cc = Catalog(self.CATALOG_URL, token=ctx['token']) # first create simple module_name lookup based on hash (TODO: in catalog, allow us to only fetch dynamic service modules) modules = cc.list_basic_module_info({ 'include_released': 1, 'include_unreleased': 1 }) module_hash_lookup = {} # hash => module_name for m in modules: if 'dynamic_service' not in m or m['dynamic_service'] != 1: continue module_hash_lookup[self.get_module_name_hash( m['module_name'])] = m['module_name'] # next get environment id (could be a config parameter in the future rather than looping over everything) result = [] # not sure if this is supposed to pass False, or the string 'false' slists = rancher.list_environment(system='false') if len(slists) == 0: return [] # I shouldn't return for slist in slists: eid = slist['id'] # get service info entries = rancher.list_service(environmentId=eid) if len(entries) == 0: continue for entry in entries: rs = entry['name'].split('-') if len(rs) != 2: continue es = { 'status': entry['state'], 'health': entry['healthState'], 'hash': rs[1] } #if es['health'] == 'healthy' and es['status'] == 'active': if es['status'] == 'active': es['up'] = 1 else: es['up'] = 0 try: mv = cc.get_module_version({ 'module_name': module_hash_lookup[rs[0]], 'version': rs[1] }) es['url'] = self.get_service_url(mv) es['version'] = mv['version'] es['module_name'] = mv['module_name'] es['release_tags'] = mv['release_tags'] es['git_commit_hash'] = mv['git_commit_hash'] except: # this will occur if the module version is not registered with the catalog, or if the module # was not marked as a service, or if something was started in Rancher directly and pulled # from somewhere else, or an old version of the catalog was used to start this service es['url'] = "https://{0}:{1}/dynserv/{3}.{2}".format( self.SVC_HOSTNAME, self.NGINX_PORT, rs[0], rs[1]) es['version'] = '' es['release_tags'] = [] es['git_commit_hash'] = '' es['module_name'] = '!' + rs[0] + '' result.append(es) returnVal = result #END list_service_status # At some point might do deeper type checking... if not isinstance(returnVal, list): raise ValueError('Method list_service_status return value ' + 'returnVal is not type list as required.') # return the results return [returnVal]