def execute(self): """ give the user name and role, create password and create a record in dbauth, return username, password, role """ self.result.format = skytask.output_format_json if self.apply: try: # attempt to new user records from options create_user(self.username, self.secret, self.email, self.role) # attempt to read newly created record user_record = find_user(self.username)[0] # check for non-empty list of records; find_user() returns single/unique if user_record: self.result.status = sky_cfg.API_STATUS_SUCCESS self.result.output = 'user added: {0}'.format(find_user(self.username)[0]) else: self.result.status = sky_cfg.API_STATUS_FAIL self.result.output = 'failure to create new user record: {0}'.format( simple_error_format(SkyBaseUserIdNotFoundError(self.username)) ) except (skybase.exceptions.SkyBaseError, sqlite3.Error) as e: self.result.output = simple_error_format(e) self.result.status = sky_cfg.API_STATUS_FAIL else: self.result.status = sky_cfg.API_STATUS_SUCCESS self.result.output = 'user record to be added: {0}; --apply required'.format(self.username) return self.result
def delete_chef_nodes(planet, runtime, nodes): result = dict() for node in nodes: result[node] = dict() try: if runtime.apply: try: result[node][ 'delete_node'] = skybase.actions.sky_chef.delete_node( planet=planet, node=node) except Exception as e: result[node]['delete_node'] = simple_error_format(e) else: try: result[node][ 'delete_client'] = skybase.actions.sky_chef.delete_client( planet=planet, client=node) except Exception as e: result[node]['delete_client'] = simple_error_format(e) else: result[node]['get_node'] = skybase.actions.sky_chef.get_node( planet=planet, node=node) except Exception as e: result[node] = simple_error_format(e) return result
def preflight_check(self): preflight_result = [] # instantiate planet try: self.planet = Planet(self.args.get('planet_name')) except Exception as e: self.preflight_check_result.status = 'FAIL' preflight_result.append( skybase.exceptions.SkyBaseValidationError( 'planet init: {0}'.format(simple_error_format(e)))) # validate stacks for errors before writing to service state registry try: are_stacks_valid = skybase.actions.skycloud.are_stacks_valid( self.planet.orchestration_engine, self.stacks) if not are_stacks_valid: self.preflight_check_result.status = 'FAIL' preflight_result.append( skybase.exceptions.SkyBaseValidationError( 'cannot write service state record with invalid stacks' )) except Exception as e: self.preflight_check_result.status = 'FAIL' preflight_result.append( skybase.exceptions.SkyBaseValidationError( 'test for valid stacks: {0}'.format( simple_error_format(e)))) self.preflight_check_result.set_output(preflight_result) return self.preflight_check_result
def execute(self, skytask_name, skytask_args, **kwargs): try: # initialize skytask from class name and submitted args skytask = Runner(skytask_name, skytask_args) except skybase.exceptions.SkyBaseError as e: # create a result object to handle Runner() init exceptions execute_result = TaskResult() execute_result.status = 'FAIL' execute_result.output = simple_error_format(e) logger.warning('Runner({0}) failed to instantiate: {1}'.format( skytask_name, simple_error_format(e))) else: # attempt to execute successfully created skytask execute_result = skytask.execute() if len(execute_result.next_task_name) > 0: # queue up next/postproc tasks # execute task in async mode, routing it to derived message queue # TODO: route to correct queue based on next_task_name and planet args if exist celery_result = execute.apply_async( args=[execute_result.next_task_name, execute_result.next_args], queue='admin', kwargs={}, ) logger.info('{0} next task_id: {1}'.format(skytask_name, celery_result)) return execute_result.convert_to_string()
def execute(self): """ give the user name and role, create password and create a record in dbauth, return username, password, role """ self.result.format = skytask.output_format_json if self.apply: try: # attempt to new user records from options create_user(self.username, self.secret, self.email, self.role) # attempt to read newly created record user_record = find_user(self.username)[0] # check for non-empty list of records; find_user() returns single/unique if user_record: self.result.status = sky_cfg.API_STATUS_SUCCESS self.result.output = 'user added: {0}'.format( find_user(self.username)[0]) else: self.result.status = sky_cfg.API_STATUS_FAIL self.result.output = 'failure to create new user record: {0}'.format( simple_error_format( SkyBaseUserIdNotFoundError(self.username))) except (skybase.exceptions.SkyBaseError, sqlite3.Error) as e: self.result.output = simple_error_format(e) self.result.status = sky_cfg.API_STATUS_FAIL else: self.result.status = sky_cfg.API_STATUS_SUCCESS self.result.output = 'user record to be added: {0}; --apply required'.format( self.username) return self.result
def preflight_check(self): # initialize results container preflight_result = [] # instantiate planet try: self.planet = Planet(self.args.get('planet')) except Exception as e: self.preflight_check_result.status = 'FAIL' preflight_result.append( skybase.exceptions.SkyBaseValidationError( 'planet init: {0}'.format(simple_error_format(e)))) try: self.node = sky_chef_actions.get_node(self.planet, self.args.get('node'), self.logger) except Exception as e: self.preflight_check_result.status = 'FAIL' preflight_result.append( skybase.exceptions.SkyBaseValidationError( 'could not find node "{0}": {1}'.format( self.args.get('node'), simple_error_format(e)))) self.preflight_check_result.set_output(preflight_result) return self.preflight_check_result
def execute(self, skytask_name, skytask_args, **kwargs): try: # initialize skytask from class name and submitted args skytask = Runner(skytask_name, skytask_args) except skybase.exceptions.SkyBaseError as e: # create a result object to handle Runner() init exceptions execute_result = TaskResult() execute_result.status = 'FAIL' execute_result.output = simple_error_format(e) logger.warning('Runner({0}) failed to instantiate: {1}'.format(skytask_name, simple_error_format(e))) else: # attempt to execute successfully created skytask execute_result = skytask.execute() if len(execute_result.next_task_name) > 0: # queue up next/postproc tasks # execute task in async mode, routing it to derived message queue # TODO: route to correct queue based on next_task_name and planet args if exist celery_result = execute.apply_async( args=[execute_result.next_task_name, execute_result.next_args], queue='admin', kwargs={}, ) logger.info('{0} next task_id: {1}'.format(skytask_name, celery_result)) return execute_result.convert_to_string()
def execute(self): try: sky_chef_actions.delete_node(self.planet, self.args.get('node'), self.logger) except Exception as e: self.result.status = sky_cfg.API_STATUS_FAIL raise skybase.exceptions.SkyBaseResponseError( 'Could not delete node "{0}" from Chef: {1}'.format( self.args.get('node'), simple_error_format(e))) try: sky_chef_actions.delete_client(self.planet, self.args.get('node'), self.logger) except Exception as e: self.result.status = sky_cfg.API_STATUS_FAIL raise skybase.exceptions.SkyBaseResponseError( 'Could not delete node client "{0}" from Chef: {1}'.format( self.args.get('node'), simple_error_format(e))) self.result.status = sky_cfg.API_STATUS_SUCCESS self.result.output = 'Node "{0}" has been deleted from Chef'.format( self.args.get('node')) return self.result
def execute(self): """ update user email and/or role """ self.result.format = skytask.output_format_json if self.apply: try: # attempt to update user information if self.email: update_email(self.username, self.email) if self.role: upsert_userroles(self.username, self.role) self.result.status = sky_cfg.API_STATUS_SUCCESS self.result.output = 'user updated: {0}'.format(find_user(self.username)[0]) except (skybase.exceptions.SkyBaseError, sqlite3.Error) as e: self.result.output = simple_error_format(e) self.result.status = sky_cfg.API_STATUS_FAIL else: self.result.status = sky_cfg.API_STATUS_SUCCESS self.result.output = 'user not updated: {0}; --apply required'.format(find_user(self.username)[0]) return self.result
def execute(self): """ delete one user record """ self.result.format = skytask.output_format_raw if self.apply: try: # attempt to delete record using username delete_user(self.username) if not unique_user_exists(self.username): self.result.status = sky_cfg.API_STATUS_SUCCESS self.result.output = 'user record deleted: {0}'.format(self.username) else: self.result.status = sky_cfg.API_STATUS_FAIL self.result.output = 'delete attempt failed user: {0}'.format(self.username) except (skybase.exceptions.SkyBaseError, sqlite3.Error) as e: self.result.output = simple_error_format(e) self.result.status = sky_cfg.API_STATUS_FAIL else: self.result.status = sky_cfg.API_STATUS_SUCCESS self.result.output = 'user record to be deleted: {0}; --apply required'.format(self.username) return self.result
def execute(self): """ delete one user record """ self.result.format = skytask.output_format_raw if self.apply: try: # attempt to delete record using username delete_user(self.username) if not unique_user_exists(self.username): self.result.status = sky_cfg.API_STATUS_SUCCESS self.result.output = 'user record deleted: {0}'.format( self.username) else: self.result.status = sky_cfg.API_STATUS_FAIL self.result.output = 'delete attempt failed user: {0}'.format( self.username) except (skybase.exceptions.SkyBaseError, sqlite3.Error) as e: self.result.output = simple_error_format(e) self.result.status = sky_cfg.API_STATUS_FAIL else: self.result.status = sky_cfg.API_STATUS_SUCCESS self.result.output = 'user record to be deleted: {0}; --apply required'.format( self.username) return self.result
def delete_stack_chef_roles(planet, runtime, stacks): result = dict() for skybase_stack_id in stacks.keys(): skybase_id, stack_name = os.path.split(skybase_stack_id) service = ServiceRegistryRecord.init_from_id(skybase_id) for role in service.blueprint.stacks[stack_name]['roles']: chef_role_name = make_chef_role_name( role_name=role, deploy_tag=service.tag, ) try: if runtime.apply: if skybase.actions.skycloud.is_stack_deleted_or_not_found(planet, stack_name): result[chef_role_name] = skybase.actions.sky_chef.delete_role(planet=planet, role=chef_role_name) else: result[chef_role_name] = 'stack {0} exists. cannot delete chef role {1}'.format(stack_name, chef_role_name) else: result[chef_role_name] = skybase.actions.sky_chef.get_role(planet=planet, role=chef_role_name) except Exception as e: result[chef_role_name] = simple_error_format(e) return result
def execute(self): """ update user email and/or role """ self.result.format = skytask.output_format_json if self.apply: try: # attempt to update user information if self.email: update_email(self.username, self.email) if self.role: upsert_userroles(self.username, self.role) self.result.status = sky_cfg.API_STATUS_SUCCESS self.result.output = 'user updated: {0}'.format( find_user(self.username)[0]) except (skybase.exceptions.SkyBaseError, sqlite3.Error) as e: self.result.output = simple_error_format(e) self.result.status = sky_cfg.API_STATUS_FAIL else: self.result.status = sky_cfg.API_STATUS_SUCCESS self.result.output = 'user not updated: {0}; --apply required'.format( find_user(self.username)[0]) return self.result
def delete_stack_chef_roles(planet, runtime, stacks): result = dict() for skybase_stack_id in stacks.keys(): skybase_id, stack_name = os.path.split(skybase_stack_id) service = ServiceRegistryRecord.init_from_id(skybase_id) for role in service.blueprint.stacks[stack_name]['roles']: chef_role_name = make_chef_role_name( role_name=role, deploy_tag=service.tag, ) try: if runtime.apply: if skybase.actions.skycloud.is_stack_deleted_or_not_found( planet, stack_name): result[ chef_role_name] = skybase.actions.sky_chef.delete_role( planet=planet, role=chef_role_name) else: result[ chef_role_name] = 'stack {0} exists. cannot delete chef role {1}'.format( stack_name, chef_role_name) else: result[chef_role_name] = skybase.actions.sky_chef.get_role( planet=planet, role=chef_role_name) except Exception as e: result[chef_role_name] = simple_error_format(e) return result
def execute(self): self.result.output = dict() self.result.format = skytask.output_format_json self.result.output[ 'chef_update'] = skybase.actions.skychef.chef_update( planet=self.planet, service=self.source_service, runtime=self.runtime, config=self.runner_cfg, ) self.result.output[ 'package_transfer'] = skybase.actions.skycloud.package_transfer( planet=self.planet, service=self.source_service, runtime=self.runtime, system=self.system, ) self.result.output[ 'salt_grains_transfer'] = skybase.actions.skycloud.salt_grains_transfer( planet=self.planet, service=self.source_service, runtime=self.runtime, system=self.system, ) self.result.output[ 'salt_update_service'] = skybase.actions.salt.update_service( service=self.target_service, stacks=self.source_service.deploy.stack_launch_list, runtime=self.runtime, update_plan=self.update_plan, authtoken=self.authtoken, ) # modify service registry record on successful update if self.runtime.apply: self.target_service.metadata.version = self.source_service.manifest.get( 'app_version') self.target_service.metadata.build = self.source_service.manifest.get( 'build_id') self.target_service.metadata.artiball = self.source_artiball # attempt to update state db record try: self.result.output['update_service_registry'] = \ skybase.actions.state.update( mode=self.mode, record_id=self.id, service_record=self.target_service.serialize_as_yaml(), credentials=sky_cfg.SkyConfig.init_from_file('credentials').data, ) except SkyBaseError as e: self.result.output[ 'update_service_registry'] = simple_error_format(e) self.result.format = skytask.output_format_json return self.result
def get_chef_nodes(planet, nodes): result = dict() for node in nodes: try: result[node] = skybase.actions.sky_chef.get_node(planet, node) except Exception as e: result[node] = simple_error_format(e) return result
def get_restapi_execute_result(self, response): # extract results from backend db using execution link in response try: # result = self.pollwait(json.loads(response)['links']['result']['href']) result = self.pollwait(response['links']['result']['href']) result_data = result['data'] return result_data except Exception as e: raise SkyBaseResponseError('{0} when accessing execution results'.format(simple_error_format(e)))
def get_restapi_execute_result(self, response): # extract results from backend db using execution link in response try: # result = self.pollwait(json.loads(response)['links']['result']['href']) result = self.pollwait(response['links']['result']['href']) result_data = result['data'] return result_data except Exception as e: raise SkyBaseResponseError( '{0} when accessing execution results'.format( simple_error_format(e)))
def preflight_check(self): # initialize results container preflight_result = [] if not unique_user_exists(self.username): self.preflight_check_result.status = 'FAIL' preflight_result.append(simple_error_format( SkyBaseUserAuthorizationError( 'cannot find username: {0}'.format(self.username)))) self.preflight_check_result.set_output(preflight_result) return self.preflight_check_result
def preflight_check(self): # initialize results container preflight_result = [] if not unique_user_exists(self.username): self.preflight_check_result.status = 'FAIL' preflight_result.append( simple_error_format( SkyBaseUserAuthorizationError( 'cannot find username: {0}'.format(self.username)))) self.preflight_check_result.set_output(preflight_result) return self.preflight_check_result
def execute(self): """ list all users and roles from auth db """ self.result.format = skytask.output_format_json try: self.result.output = list_users(self.username) self.result.status = sky_cfg.API_STATUS_SUCCESS except (skybase.exceptions.SkyBaseError, sqlite3.Error) as e: self.result.output = simple_error_format(e) self.result.status = sky_cfg.API_STATUS_FAIL return self.result
def update(self, record_id, record_object, **kwargs): try: logger.info('attempt to instantiate update state db record ({0}, {1})'.format(id, kwargs)) # attempt to read state db record from local resources result = skybase.actions.state.local.update(record_id, record_object, **kwargs) except skybase.exceptions.SkyBaseError as e: logger.info('failed to instantiate ServiceRegistryRecord({0})'.format(id)) result = simple_error_format(e) return result
def create(self, planet_name, service_name, tag, registration, provider, stacks, **kwargs): try: logger.info('attempt to instantiate update state db record ({0}, {1})'.format(id, kwargs)) # attempt to read state db record from local resources result = skybase.actions.state.local.create(planet_name, service_name, tag, registration, provider, stacks, **kwargs) except skybase.exceptions.SkyBaseError as e: logger.info('failed to create ServiceRegistryRecord({0})'.format(id)) result = simple_error_format(e) return result
def pollwait(self, url, timeout=None, retry_interval=RETRY): if timeout is None: timeout = min( self.cli_cfg.data.get('result_fetch_timeout', SkyCmd.TIMEOUT_DEFAULT), SkyCmd.TIMEOUT_MAX) # wait for celery task status SUCCESS over timeout interval start_time = time.time() while time.time() - start_time < timeout: execute_result = self.probe(url) try: status = execute_result['metadata']['task_status'] except KeyError as e: raise SkyBaseResponseError( '{0} when accessing response status'.format( simple_error_format(e))) if status == 'PENDING': # TODO: progress bar would be nice, but needs to not interfere with json prettyprint time.sleep(retry_interval) elif status == 'SUCCESS': # return execute task results data if exists try: return execute_result except KeyError as e: raise SkyBaseResponseError( '{0} when accessing response results'.format( simple_error_format(e))) else: raise SkyBaseError('unknown task status: {0}'.format(status)) raise SkyBaseTimeOutError( 'attempt to fetch results failed after {0}s'.format(timeout))
def delete_chef_nodes(planet, runtime, nodes): result = dict() for node in nodes: result[node] = dict() try: if runtime.apply: try: result[node]['delete_node'] = skybase.actions.sky_chef.delete_node(planet=planet, node=node) except Exception as e: result[node]['delete_node'] = simple_error_format(e) else: try: result[node]['delete_client'] = skybase.actions.sky_chef.delete_client(planet=planet, client=node) except Exception as e: result[node]['delete_client'] = simple_error_format(e) else: result[node]['get_node'] = skybase.actions.sky_chef.get_node(planet=planet, node=node) except Exception as e: result[node] = simple_error_format(e) return result
def preflight_check(self): # initialize results container preflight_result = [] # instantiate planet try: self.planet = Planet(self.args.get('planet_name')) except Exception as e: self.preflight_check_result.status = 'FAIL' preflight_result.append( skybase.exceptions.SkyBaseValidationError( 'planet init: {0}'.format(simple_error_format(e)))) self.preflight_check_result.set_output(preflight_result) return self.preflight_check_result
def init_from_file(cls, schema_name, config_dir=CONFIG_DIR): # prepare configuration filename config_file_name = '/'.join([config_dir, schema_name + '.yaml']) config_file = os.path.expanduser(config_file_name) # read in target configuration filea and attempt to init class try: runner_config_data = read_yaml_from_file(config_file) except (IOError, ScannerError, ParserError) as e: # wrap all expected errors as SkyBaseError type raise SkyBaseConfigurationError(simple_error_format(e)) cfg = cls(schema_name, runner_config_data) return cfg
def preflight_check(self): preflight_result = [] # require that username exists before resetting secret if not unique_user_exists(self.username): self.preflight_check_result.status = 'FAIL' preflight_result.append(simple_error_format( skybase.exceptions.SkyBaseUserIdNotFoundError( 'username {0} not found'.format(self.username)))) if not self.secret: self.secret = generate_random_key() self.preflight_check_result.set_output(preflight_result) return self.preflight_check_result
def preflight_check(self): # initialize results container preflight_result = [] # validate that username doesn't exist if unique_user_exists(self.username): self.preflight_check_result.status = 'FAIL' preflight_result.append(simple_error_format( SkyBaseUserAuthorizationError( 'username {0} exists'.format(self.username)))) if not self.secret: # if secret not defined, generate a secret key self.secret = generate_random_key() self.preflight_check_result.set_output(preflight_result) return self.preflight_check_result
def execute(self): """ reset secret for existing user. """ self.result.format = skytask.output_format_json if self.apply: try: reset_password(self.username, self.secret) self.result.output = 'user secret for {0}: {1}'.format(self.username, self.secret) self.result.status = sky_cfg.API_STATUS_SUCCESS except (skybase.exceptions.SkyBaseError, sqlite3.Error) as e: self.result.output = '{0}; (username, secret) = ({1}, {2})'.format(simple_error_format(e), self.username, self.secret) self.result.status = sky_cfg.API_STATUS_FAIL else: self.result.output = 'user secret target for {0}: {1}; --apply required'.format(self.username, self.secret) self.result.status = sky_cfg.API_STATUS_SUCCESS return self.result
def preflight_check(self): # initialize results container preflight_result = [] # validate that username doesn't exist if unique_user_exists(self.username): self.preflight_check_result.status = 'FAIL' preflight_result.append( simple_error_format( SkyBaseUserAuthorizationError('username {0} exists'.format( self.username)))) if not self.secret: # if secret not defined, generate a secret key self.secret = generate_random_key() self.preflight_check_result.set_output(preflight_result) return self.preflight_check_result
def preflight_check(self): # initialize results container preflight_result = [] # validate that username exists if not unique_user_exists(self.username): self.preflight_check_result.status = 'FAIL' preflight_result.append(simple_error_format( SkyBaseUserAuthorizationError( 'cannot find username: {0}'.format(self.username)))) # require at least something to update if self.role is None and self.email is None: self.preflight_check_result.status = 'FAIL' preflight_result.append('choose at least one update option: --role, --email') self.preflight_check_result.set_output(preflight_result) return self.preflight_check_result
def update(self): try: self.metadata.update() self.blueprint.update() # TODO: COOKBOOK_ONLY first and only update type supported. will eventually derive from --plan option and artiball config log_entry = '{0}\t{1}\t{2}\t{3}\t{4}\t{5}\t{6}\n'.format( basic_timestamp(), 'UPDATE_RERUN', self.service, self.metadata.version, self.metadata.build, self.tag, self.metadata.artiball, ) self.log.update(log_entry) return True except Exception as e: return simple_error_format(e)
def execute(self): self.result.output = dict() self.result.format = skytask.output_format_json self.result.output["chef_update"] = skybase.actions.skychef.chef_update( planet=self.planet, service=self.source_service, runtime=self.runtime, config=self.runner_cfg ) self.result.output["package_transfer"] = skybase.actions.skycloud.package_transfer( planet=self.planet, service=self.source_service, runtime=self.runtime, system=self.system ) self.result.output["salt_grains_transfer"] = skybase.actions.skycloud.salt_grains_transfer( planet=self.planet, service=self.source_service, runtime=self.runtime, system=self.system ) self.result.output["salt_update_service"] = skybase.actions.salt.update_service( service=self.target_service, stacks=self.source_service.deploy.stack_launch_list, runtime=self.runtime, update_plan=self.update_plan, authtoken=self.authtoken, ) # modify service registry record on successful update if self.runtime.apply: self.target_service.metadata.version = self.source_service.manifest.get("app_version") self.target_service.metadata.build = self.source_service.manifest.get("build_id") self.target_service.metadata.artiball = self.source_artiball # attempt to update state db record try: self.result.output["update_service_registry"] = skybase.actions.state.update( mode=self.mode, record_id=self.id, service_record=self.target_service.serialize_as_yaml(), credentials=sky_cfg.SkyConfig.init_from_file("credentials").data, ) except SkyBaseError as e: self.result.output["update_service_registry"] = simple_error_format(e) self.result.format = skytask.output_format_json return self.result
def preflight_check(self): # initialize results container preflight_result = [] # validate that username exists if not unique_user_exists(self.username): self.preflight_check_result.status = 'FAIL' preflight_result.append( simple_error_format( SkyBaseUserAuthorizationError( 'cannot find username: {0}'.format(self.username)))) # require at least something to update if self.role is None and self.email is None: self.preflight_check_result.status = 'FAIL' preflight_result.append( 'choose at least one update option: --role, --email') self.preflight_check_result.set_output(preflight_result) return self.preflight_check_result
def submit_http_request(self): # prepare request data/body data = { 'args': vars(self.args), 'metadata': self.prepare_job_metadata() } # sign data request body with user credentials request_signature = skybase.api.create_signature( self.sky_credentials.data['key'], json.dumps(data)) # prepare request header headers = { sky_cfg.API_HTTP_HEADER_ACCESS_KEY: self.sky_credentials.data['user_id'], sky_cfg.API_HTTP_HEADER_SIGNATURE: request_signature, } # get default restapi task url restapi_url = skybase.api.create_api_url( server=self.cli_cfg.get_data_value( 'restapi_server_url', data_default=sky_cfg.API_SERVER), route=sky_cfg.API_ROUTES.get('task'), ) # gather query parameter string params = self.get_request_params() # post request to restapi try: response = requests.post( restapi_url, params=params, headers=headers, data=json.dumps(data), ) except requests.exceptions.ConnectionError as e: raise SkyBaseRestAPIError(simple_error_format(e)) return response
def execute(self): # attempt to read state db record try: result = skybase.actions.state.read( mode=self.mode, record_id=self.id, credentials=sky_cfg.SkyConfig.init_from_file('credentials').data, format=self.format ) except skybase.exceptions.SkyBaseError as e: result = simple_error_format(e) # set result output mode; --format default = json if self.format == 'yaml': self.result.format = skytask.output_format_raw else: self.result.format = skytask.output_format_json self.result.output = result return self.result
def submit_http_request(self): # prepare request data/body data = { 'args': vars(self.args), 'metadata': self.prepare_job_metadata() } # sign data request body with user credentials request_signature = skybase.api.create_signature(self.sky_credentials.data['key'], json.dumps(data)) # prepare request header headers = { sky_cfg.API_HTTP_HEADER_ACCESS_KEY: self.sky_credentials.data['user_id'], sky_cfg.API_HTTP_HEADER_SIGNATURE: request_signature, } # get default restapi task url restapi_url = skybase.api.create_api_url( server=self.cli_cfg.get_data_value('restapi_server_url', data_default=sky_cfg.API_SERVER), route=sky_cfg.API_ROUTES.get('task'), ) # gather query parameter string params = self.get_request_params() # post request to restapi try: response = requests.post( restapi_url, params=params, headers=headers, data=json.dumps(data), ) except requests.exceptions.ConnectionError as e: raise SkyBaseRestAPIError(simple_error_format(e)) return response
def preflight_check(self): # initialize results container preflight_result = [] # instantiate planet try: self.planet = Planet(self.args.get('planet_name')) except Exception as e: self.preflight_check_result.status = 'FAIL' preflight_result.append(skybase.exceptions.SkyBaseValidationError('planet init: {0}'.format(simple_error_format(e)))) self.preflight_check_result.set_output(preflight_result) return self.preflight_check_result
def preflight_check(self): preflight_result = [] # TODO: move reusable preflight functions/tests to skybase.skytask.service # instantiate planet from --planet option try: self.planet = Planet(self.planet_name) except Exception as e: self.preflight_check_result.status = "FAIL" preflight_result.append(SkyBaseValidationError("source planet init: {0}".format(simple_error_format(e)))) try: # attempt transfer artiball to worker if not os.path.exists( os.path.join(self.runner_cfg.data["artiball_data_dir"], self.args.get("source_artiball")) ): artiball_transfer( artiball_key=self.args.get("source_artiball"), bucket_name=self.runner_cfg.data["buckets"]["cache"]["name"], profile=self.runner_cfg.data["buckets"]["cache"]["profile"], release_dir=self.runner_cfg.data["artiball_data_dir"], ) except boto.exception.S3ResponseError as e: self.preflight_check_result.status = "FAIL" preflight_result.append( SkyBaseValidationError("artiball transfer: {0}: {1}".format(type(e).__name__, str(e.message))) ) except Exception as e: self.preflight_check_result.status = "FAIL" preflight_result.append(SkyBaseValidationError("artiball transfer: {0}".format(simple_error_format(e)))) else: # initialize SkyService from source artiball try: self.source_service = SkyService().init_from_artiball( artiball_name=self.source_artiball, artiball_data_dir=self.runner_cfg.data["artiball_data_dir"] ) self.chef_type = self.source_service.deploy.definition.get("chef_type", "server") except Exception as e: self.preflight_check_result.status = "FAIL" preflight_result.append(SkyBaseValidationError("sky service init: {0}".format(simple_error_format(e)))) else: # test CLI stack option values against SkyService stacks try: bad_stacks = [] for stack in self.args["stack_list"]: if stack not in self.source_service.deploy.stack_ids: bad_stacks.append(stack) if bad_stacks: self.preflight_check_result.status = "FAIL" preflight_result.append( SkyBaseValidationError( "source service {0}: unknown stacks {1}".format(self.source_service.name, bad_stacks) ) ) else: # given all good stacks, prepare stack launch list target self.source_service.deploy.stack_launch_list = ( self.args["stack_list"] if self.args["stack_list"] else self.source_service.deploy.stack_ids ) except Exception as e: self.preflight_check_result.status = "FAIL" preflight_result.append( SkyBaseValidationError("source stack verification: {0}".format(simple_error_format(e))) ) # attempt to read state db record try: serialized_record = skybase.actions.state.read( mode=self.mode, record_id=self.id, credentials=sky_cfg.SkyConfig.init_from_file("credentials").data, format="yaml", ) # DECISION: need general method for processing API in-flight errors if serialized_record.startswith("StateDBRecordNotFoundError"): raise StateDBRecordNotFoundError(self.id) except Exception as e: self.preflight_check_result.status = "FAIL" preflight_result.append(SkyBaseValidationError(simple_error_format(e))) else: try: self.target_service = yaml.load(serialized_record) self.runtime.tag = self.target_service.tag except Exception as e: self.preflight_check_result.status = "FAIL" preflight_result.append( SkyBaseValidationError("service registry init: {0}".format(simple_error_format(e))) ) else: try: bad_stacks = [] for stack in self.source_service.deploy.stack_launch_list: if stack not in self.target_service.stacks.deployed_stacks: bad_stacks.append(stack) if bad_stacks: self.preflight_check_result.status = "FAIL" preflight_result.append( SkyBaseValidationError( "target service {0}: stacks not deployed {1}".format( self.target_service.service, bad_stacks ) ) ) except Exception as e: self.preflight_check_result.status = "FAIL" preflight_result.append( SkyBaseValidationError("target stack verification: {0}".format(simple_error_format(e))) ) # instantiate planet based on service registry value try: self.target_planet = Planet(self.target_service.planet) except Exception as e: self.preflight_check_result.status = "FAIL" preflight_result.append( SkyBaseValidationError("target planet init: {0}".format(simple_error_format(e))) ) else: if self.planet.planet_name != self.target_planet.planet_name: self.preflight_check_result.status = "FAIL" preflight_result.append( "source planet {0} not equal target planet {1}".format( self.planet.planet_name, self.target_planet.planet_name ) ) # test if existing service can be updated if self.source_service and self.target_service: # TODO: test that target and source service names match! is_version_ok = self.source_service.manifest.get("app_version") >= self.target_service.metadata.version if not is_version_ok: self.preflight_check_result.status = "FAIL" preflight_result.append( SkyBaseValidationError( "source < target service version: {0} < {1}".format( self.source_service.manifest.get("app_version"), self.target_service.metadata.version ) ) ) else: self.preflight_check_result.status = "FAIL" preflight_result.append( SkyBaseValidationError( "cannot update service: missing (target / source): ({0} / {1})".format( self.target_service == None, self.source_service == None ) ) ) try: # acquire salt API authtoken self.authtoken = skybase.actions.salt.get_saltapi_authtoken( runner_cfg=self.runner_cfg, planet_name=self.target_service.planet ) if self.authtoken is None: self.preflight_check_result.status = "FAIL" preflight_result.append(SkyBaseValidationError("failed to login to salt API")) except Exception as e: self.preflight_check_result.status = "FAIL" preflight_result.append( SkyBaseValidationError("failed to acquire salt API authtoken: {0}".format(simple_error_format(e))) ) else: for stack in self.source_service.deploy.stack_launch_list: stack_roles = self.target_service.blueprint.get_stack_roles(stack) for role in stack_roles: stack_role_grain = self.target_service.stacks.stacks[stack].get_stack_role_salt_grain_skybase_id( role ) try: saltapi_result = skybase.actions.salt.test_ping_by_grain( grain=stack_role_grain, authtoken=self.authtoken, planet_name=self.target_service.planet ) except Exception as e: self.preflight_check_result.status = "FAIL" preflight_result.append( SkyBaseValidationError("saltapi test.ping: {0}".format(simple_error_format(e))) ) else: # verify that some minions were targeted if saltapi_result[0] != SkySaltAPI.NO_MINIONS: # verify all minions reply returned True to ping if not all(saltapi_result[0].values()): self.preflight_check_result.status = "FAIL" preflight_result.append( SkyBaseValidationError( "unreachable salt minions for stack-role {0}-{1} using grain {2}: {3}".format( stack, role, stack_role_grain, saltapi_result ) ) ) else: self.preflight_check_result.status = "FAIL" preflight_result.append( SkyBaseValidationError( "{0} using grain: {1}".format(SkySaltAPI.NO_MINIONS, stack_role_grain) ) ) self.preflight_check_result.set_output(preflight_result) return self.preflight_check_result
def preflight_check(self): # initialize results container preflight_result = [] # instantiate planet try: self.planet = Planet(self.args.get('destination_planet')) except Exception as e: self.preflight_check_result.status = 'FAIL' preflight_result.append(skybase.exceptions.SkyBaseValidationError('planet init: {0}'.format(simple_error_format(e)))) # TODO: should artiball transfer *always* occur? OR attempt comparison timestamp / fingerprint to limit? # attempt transfer artiball to worker try: if not os.path.exists(os.path.join(self.runner_cfg.data['artiball_data_dir'], self.args.get('source_artiball'))): skybase.actions.skyenv.artiball_transfer( artiball_key=self.args.get('source_artiball'), bucket_name=self.runner_cfg.data['buckets']['cache']['name'], profile=self.runner_cfg.data['buckets']['cache']['profile'], release_dir=self.runner_cfg.data['artiball_data_dir'] ) except Exception as e: self.preflight_check_result.status = 'FAIL' preflight_result.append(skybase.exceptions.SkyBaseValidationError('artiball transfer: {0}'.format(simple_error_format(e)))) # initialize SkyService using --artiball value try: self.service = SkyService().init_from_artiball( artiball_name=self.args.get('source_artiball'), artiball_data_dir=self.runner_cfg.data['artiball_data_dir'] ) # check planet trusted chef cookbook source against artiball's metadata when running chef-server mode if self.service.deploy.definition.get('chef_type', 'server') == 'server': cookbook_source = self.service.manifest['chef_cookbook_source'] trusted_cookbook_source = self.planet._yaml_data['services']['chefserver']['trusted_chef_cookbook_source'] if cookbook_source not in trusted_cookbook_source: self.preflight_check_result.status = 'FAIL' preflight_result.append('untrusted chef cookbook source in artiball: ' + cookbook_source) except Exception as e: self.preflight_check_result.status = 'FAIL' preflight_result.append(skybase.exceptions.SkyBaseValidationError('sky service init: {0}'.format(simple_error_format(e)))) # test CLI stack option values against SkyService stacks try: bad_stacks = [] for stack in self.args['stack_list']: if stack not in self.service.deploy.stack_ids: bad_stacks.append(stack) if bad_stacks: self.preflight_check_result.status = 'FAIL' preflight_result.append(skybase.exceptions.SkyBaseValidationError('service {0}: unknown stacks {1}'.format(self.service.name, bad_stacks))) else: # given all good stacks, prepare stack launch list target self.service.deploy.stack_launch_list = self.args['stack_list'] if self.args['stack_list'] else self.service.deploy.stack_ids except Exception as e: self.preflight_check_result.status = 'FAIL' preflight_result.append(skybase.exceptions.SkyBaseValidationError('stack verification: {0}'.format(simple_error_format(e)))) # prepare runtime try: # push client runtime options into runtime object runtime_unpacked = dict(attr.split('=') for attr in self.args.get('runtime')) self.runtime.set_attrs(**runtime_unpacked) except Exception as e: self.preflight_check_result.status = 'FAIL' preflight_result.append(skybase.exceptions.SkyBaseValidationError('runtime: {0}'.format(simple_error_format(e)))) self.preflight_check_result.set_output(preflight_result) return self.preflight_check_result
def preflight_check(self): # container for preflight check issues preflight_result = [] # instantiate planet try: self.planet = Planet(self.args.get('planet_name')) except Exception as e: self.preflight_check_result.status = 'FAIL' preflight_result.append(skybase.exceptions.SkyBaseValidationError('planet init: {0}'.format(simple_error_format(e)))) # TODO: test if state record id exists return self.preflight_check_result
def execute(self): try: sky_chef_actions.delete_node(self.planet, self.args.get('node'), self.logger) except Exception as e: self.result.status = sky_cfg.API_STATUS_FAIL raise skybase.exceptions.SkyBaseResponseError('Could not delete node "{0}" from Chef: {1}'.format(self.args.get('node'), simple_error_format(e))) try: sky_chef_actions.delete_client(self.planet, self.args.get('node'), self.logger) except Exception as e: self.result.status = sky_cfg.API_STATUS_FAIL raise skybase.exceptions.SkyBaseResponseError('Could not delete node client "{0}" from Chef: {1}'.format(self.args.get('node'), simple_error_format(e))) self.result.status = sky_cfg.API_STATUS_SUCCESS self.result.output = 'Node "{0}" has been deleted from Chef'.format(self.args.get('node')) return self.result
def run(self): ''' For the record: set mode: a) restapi = submit, get JobID, forget = task is sent away b) local.sync = do something local in sync mode = task has actions executed local c) local.async.wait = submit, get jobID, go into WAIT loop = task submitted, but reply has to be reported back to cli d) local.async.nowait = submit, get jobID, forget = task executed locally, but no wait for systems outside SB ''' # Do a quick & dirty presubmit check. This is meant for allowing a # simple 'yes/no' or 'are you sure' type verification check try: precheck_runner = sky_runner.Runner(self.current_task_name, vars(self.args)) precheck_runner.task.presubmit_check() except SkyBasePresubmitCheckError as e: raise e except Exception: pass if self.args.exec_mode == 'local': # call runner for current skytask with arguments runner = sky_runner.Runner(self.current_task_name, vars(self.args)) # init results containers result_main = None result_next = None try: result_main = runner.execute() except Exception as e: raise SkyBaseResponseError( '{0} when accessing execution results'.format( simple_error_format(e))) has_next_task = len(result_main.next_task_name) > 0 if has_next_task: runner = sky_runner.Runner(result_main.next_task_name, result_main.next_args) try: result_next = runner.execute() except Exception as e: raise SkyBaseResponseError( '{0} when accessing execution results'.format( simple_error_format(e))) if result_main.format == skybase.skytask.output_format_json: if has_next_task: # prepare multiple result for output as single JSON blob summary_result = [] summary_result.append( {self.current_task_name: result_main.output}) summary_result.append( {result_main.next_task_name: result_next.output}) print skybase.utils.json_pretty_print(summary_result) else: # print main results separately result_main.print_result() else: # print results separately result_main.print_result() if has_next_task: result_next.print_result() elif self.args.exec_mode == 'restapi': response = self.submit() if self.args.nowait: print response else: # deserialize request response response_as_json = json.loads(response) # check restapi status; if success, attempt to extract results if response_as_json.get( 'status') == sky_cfg.API_STATUS_SUCCESS: result = self.get_restapi_execute_result(response_as_json) taskresult = skybase.skytask.TaskResult.init_from_json( result) else: # ... if not success, report message containing error taskresult = skybase.skytask.TaskResult( an_output=response_as_json.get('message'), a_format=skybase.skytask.output_format_json, ) taskresult.print_result()
def preflight_check(self): # initialize results container preflight_result = [] # instantiate planet try: self.planet = Planet(self.args.get('destination_planet')) except Exception as e: self.preflight_check_result.status = 'FAIL' preflight_result.append( skybase.exceptions.SkyBaseValidationError( 'planet init: {0}'.format(simple_error_format(e)))) # TODO: should artiball transfer *always* occur? OR attempt comparison timestamp / fingerprint to limit? # attempt transfer artiball to worker try: if not os.path.exists( os.path.join(self.runner_cfg.data['artiball_data_dir'], self.args.get('source_artiball'))): skybase.actions.skyenv.artiball_transfer( artiball_key=self.args.get('source_artiball'), bucket_name=self.runner_cfg.data['buckets']['cache'] ['name'], profile=self.runner_cfg.data['buckets']['cache'] ['profile'], release_dir=self.runner_cfg.data['artiball_data_dir']) except Exception as e: self.preflight_check_result.status = 'FAIL' preflight_result.append( skybase.exceptions.SkyBaseValidationError( 'artiball transfer: {0}'.format(simple_error_format(e)))) # initialize SkyService using --artiball value try: self.service = SkyService().init_from_artiball( artiball_name=self.args.get('source_artiball'), artiball_data_dir=self.runner_cfg.data['artiball_data_dir']) # check planet trusted chef cookbook source against artiball's metadata when running chef-server mode if self.service.deploy.definition.get('chef_type', 'server') == 'server': cookbook_source = self.service.manifest['chef_cookbook_source'] trusted_cookbook_source = self.planet._yaml_data['services'][ 'chefserver']['trusted_chef_cookbook_source'] if cookbook_source not in trusted_cookbook_source: self.preflight_check_result.status = 'FAIL' preflight_result.append( 'untrusted chef cookbook source in artiball: ' + cookbook_source) except Exception as e: self.preflight_check_result.status = 'FAIL' preflight_result.append( skybase.exceptions.SkyBaseValidationError( 'sky service init: {0}'.format(simple_error_format(e)))) # test CLI stack option values against SkyService stacks try: bad_stacks = [] for stack in self.args['stack_list']: if stack not in self.service.deploy.stack_ids: bad_stacks.append(stack) if bad_stacks: self.preflight_check_result.status = 'FAIL' preflight_result.append( skybase.exceptions.SkyBaseValidationError( 'service {0}: unknown stacks {1}'.format( self.service.name, bad_stacks))) else: # given all good stacks, prepare stack launch list target self.service.deploy.stack_launch_list = self.args[ 'stack_list'] if self.args[ 'stack_list'] else self.service.deploy.stack_ids except Exception as e: self.preflight_check_result.status = 'FAIL' preflight_result.append( skybase.exceptions.SkyBaseValidationError( 'stack verification: {0}'.format(simple_error_format(e)))) # prepare runtime try: # push client runtime options into runtime object runtime_unpacked = dict( attr.split('=') for attr in self.args.get('runtime')) self.runtime.set_attrs(**runtime_unpacked) except Exception as e: self.preflight_check_result.status = 'FAIL' preflight_result.append( skybase.exceptions.SkyBaseValidationError( 'runtime: {0}'.format(simple_error_format(e)))) self.preflight_check_result.set_output(preflight_result) return self.preflight_check_result
def run(self): ''' For the record: set mode: a) restapi = submit, get JobID, forget = task is sent away b) local.sync = do something local in sync mode = task has actions executed local c) local.async.wait = submit, get jobID, go into WAIT loop = task submitted, but reply has to be reported back to cli d) local.async.nowait = submit, get jobID, forget = task executed locally, but no wait for systems outside SB ''' # Do a quick & dirty presubmit check. This is meant for allowing a # simple 'yes/no' or 'are you sure' type verification check try: precheck_runner = sky_runner.Runner(self.current_task_name, vars(self.args)) precheck_runner.task.presubmit_check() except SkyBasePresubmitCheckError as e: raise e except Exception: pass if self.args.exec_mode == 'local': # call runner for current skytask with arguments runner = sky_runner.Runner(self.current_task_name, vars(self.args)) # init results containers result_main = None result_next = None try: result_main = runner.execute() except Exception as e: raise SkyBaseResponseError('{0} when accessing execution results'.format(simple_error_format(e))) has_next_task = len(result_main.next_task_name) > 0 if has_next_task: runner = sky_runner.Runner(result_main.next_task_name, result_main.next_args) try: result_next = runner.execute() except Exception as e: raise SkyBaseResponseError('{0} when accessing execution results'.format(simple_error_format(e))) if result_main.format == skybase.skytask.output_format_json: if has_next_task: # prepare multiple result for output as single JSON blob summary_result = [] summary_result.append({self.current_task_name: result_main.output}) summary_result.append({result_main.next_task_name: result_next.output}) print skybase.utils.json_pretty_print(summary_result) else: # print main results separately result_main.print_result() else: # print results separately result_main.print_result() if has_next_task: result_next.print_result() elif self.args.exec_mode == 'restapi': response = self.submit() if self.args.nowait: print response else: # deserialize request response response_as_json = json.loads(response) # check restapi status; if success, attempt to extract results if response_as_json.get('status') == sky_cfg.API_STATUS_SUCCESS: result = self.get_restapi_execute_result(response_as_json) taskresult = skybase.skytask.TaskResult.init_from_json(result) else: # ... if not success, report message containing error taskresult = skybase.skytask.TaskResult( an_output=response_as_json.get('message'), a_format=skybase.skytask.output_format_json, ) taskresult.print_result()
def preflight_check(self): # initialize results container preflight_result = [] # instantiate planet try: self.planet = Planet(self.args.get('planet')) except Exception as e: self.preflight_check_result.status = 'FAIL' preflight_result.append(skybase.exceptions.SkyBaseValidationError('planet init: {0}'.format(simple_error_format(e)))) try: self.node = sky_chef_actions.get_node(self.planet, self.args.get('node'), self.logger) except Exception as e: self.preflight_check_result.status = 'FAIL' preflight_result.append(skybase.exceptions.SkyBaseValidationError('could not find node "{0}": {1}'.format(self.args.get('node'), simple_error_format(e)))) self.preflight_check_result.set_output(preflight_result) return self.preflight_check_result
def pollwait(self, url, timeout=None, retry_interval=RETRY): if timeout is None: timeout = min(self.cli_cfg.data.get('result_fetch_timeout', SkyCmd.TIMEOUT_DEFAULT), SkyCmd.TIMEOUT_MAX) # wait for celery task status SUCCESS over timeout interval start_time = time.time() while time.time() - start_time < timeout: execute_result = self.probe(url) try: status = execute_result['metadata']['task_status'] except KeyError as e: raise SkyBaseResponseError('{0} when accessing response status'.format(simple_error_format(e))) if status == 'PENDING': # TODO: progress bar would be nice, but needs to not interfere with json prettyprint time.sleep(retry_interval) elif status == 'SUCCESS': # return execute task results data if exists try: return execute_result except KeyError as e: raise SkyBaseResponseError('{0} when accessing response results'.format(simple_error_format(e))) else: raise SkyBaseError('unknown task status: {0}'.format(status)) raise SkyBaseTimeOutError('attempt to fetch results failed after {0}s'.format(timeout))
def preflight_check(self): preflight_result = [] # instantiate planet try: self.planet = Planet(self.args.get('planet_name')) except Exception as e: self.preflight_check_result.status = 'FAIL' preflight_result.append(skybase.exceptions.SkyBaseValidationError('planet init: {0}'.format(simple_error_format(e)))) # validate stacks for errors before writing to service state registry try: are_stacks_valid = skybase.actions.skycloud.are_stacks_valid( self.planet.orchestration_engine, self.stacks) if not are_stacks_valid: self.preflight_check_result.status = 'FAIL' preflight_result.append(skybase.exceptions.SkyBaseValidationError('cannot write service state record with invalid stacks')) except Exception as e: self.preflight_check_result.status = 'FAIL' preflight_result.append(skybase.exceptions.SkyBaseValidationError('test for valid stacks: {0}'.format(simple_error_format(e)))) self.preflight_check_result.set_output(preflight_result) return self.preflight_check_result
def preflight_check(self): # container for preflight check issues preflight_result = [] # instantiate planet try: self.planet = Planet(self.args.get('planet_name')) except Exception as e: self.preflight_check_result.status = 'FAIL' preflight_result.append(skybase.exceptions.SkyBaseValidationError('planet init: {0}'.format(simple_error_format(e)))) # validate required options to delete one or all stacks if not (self.args.get('stack_name') or self.args.get('delete_all_stacks'))\ or (self.args.get('stack_name') and self.args.get('delete_all_stacks')): self.preflight_check_result.status = 'FAIL' preflight_result.append(('specify one stack or option to delete all stacks for {0}'.format(self.args.get('service_name')))) # validate existence of requested service/stack in state registry query = PlanetStateDbQuery( planet=self.args.get('planet_name'), service=self.args.get('service_name'), tag=self.runtime.tag, stack=self.args.get('stack_name'), query_type='exact', ) # verify unique pointer to service or service/stack if not query.can_find_exact(): self.preflight_check_result.status = 'FAIL' preflight_result.append((skybase.exceptions.SkyBaseDeployError( 'options do not identify a unique service or stack: {0}'.format(query.show_query_path()) ))) # TODO: push stack path into task result # save validated query path for postprocessing self.stack_path = query.query # reconfigure query to read resources for one or many service stacks query.query_type = query.WILDCARD query.query = query.make_query() # collect all stacks for deletion result_set = query.execute() # collect status of launched stacks for record in result_set: # accumulate list of provider stack ids for deletion self.stack_deletion_list.append(record.cloud.stack_id) try: # verify cloud provider DELETE* status for stack id stack_status = skybase.actions.skycloud.call_cloud_api( planet=self.planet, stack_name=record.cloud.stack_id, action='get_stack_status') except Exception as e: raise skybase.exceptions.SkyBaseDeployError(skybase.utils.simple_error_format(e)) if stack_status.startswith('DELETE'): self.preflight_check_result.status = 'FAIL' preflight_result.append(skybase.exceptions.SkyBaseDeployError( 'cannot delete stack {0} with status {1}'.format(record.cloud.stack_id, stack_status))) # accumulate stack information for deleting state db records and logging result self.stack_deletion_info[record.cloud.id] = { 'stack_id': record.cloud.stack_id, 'stack_name': record.cloud.stack_name, } # determine if deployed service used chef server. if so, then prepare to delete chef nodes # TODO: find authoritative location/source for skybase id definition # skybase state DB id skybase_id = '/{0}/{1}/{2}'.format( self.args.get('planet_name'), self.args.get('service_name'), self.runtime.tag, ) # init service registry record and examine blueprint chef type service_record = ServiceRegistryRecord.init_from_id(skybase_id) self.chef_type = service_record.blueprint.definition.get('chef_type') self.is_chef_type_server = (self.chef_type and self.chef_type == 'server') # prepopulate list of host/instance names for use in chef node delete when chef_type server # NOTE: self.stack_chef_nodes = dict() if self.is_chef_type_server: for skybase_stack_id, stack_info in self.stack_deletion_info.items(): self.stack_chef_nodes[stack_info['stack_name']] = skybase.actions.skychef.get_stack_chef_nodes( skybase_stack_id=skybase_stack_id, runner_cfg=self.runner_cfg, ) self.preflight_check_result.set_output(preflight_result) return self.preflight_check_result
def preflight_check(self): preflight_result = [] # TODO: move reusable preflight functions/tests to skybase.skytask.service # instantiate planet from --planet option try: self.planet = Planet(self.planet_name) except Exception as e: self.preflight_check_result.status = 'FAIL' preflight_result.append( SkyBaseValidationError('source planet init: {0}'.format( simple_error_format(e)))) try: # attempt transfer artiball to worker if not os.path.exists( os.path.join(self.runner_cfg.data['artiball_data_dir'], self.args.get('source_artiball'))): artiball_transfer( artiball_key=self.args.get('source_artiball'), bucket_name=self.runner_cfg.data['buckets']['cache'] ['name'], profile=self.runner_cfg.data['buckets']['cache'] ['profile'], release_dir=self.runner_cfg.data['artiball_data_dir']) except boto.exception.S3ResponseError as e: self.preflight_check_result.status = 'FAIL' preflight_result.append( SkyBaseValidationError('artiball transfer: {0}: {1}'.format( type(e).__name__, str(e.message)))) except Exception as e: self.preflight_check_result.status = 'FAIL' preflight_result.append( SkyBaseValidationError('artiball transfer: {0}'.format( simple_error_format(e)))) else: # initialize SkyService from source artiball try: self.source_service = SkyService().init_from_artiball( artiball_name=self.source_artiball, artiball_data_dir=self.runner_cfg.data['artiball_data_dir'] ) self.chef_type = self.source_service.deploy.definition.get( 'chef_type', 'server') except Exception as e: self.preflight_check_result.status = 'FAIL' preflight_result.append( SkyBaseValidationError('sky service init: {0}'.format( simple_error_format(e)))) else: # test CLI stack option values against SkyService stacks try: bad_stacks = [] for stack in self.args['stack_list']: if stack not in self.source_service.deploy.stack_ids: bad_stacks.append(stack) if bad_stacks: self.preflight_check_result.status = 'FAIL' preflight_result.append( SkyBaseValidationError( 'source service {0}: unknown stacks {1}'. format(self.source_service.name, bad_stacks))) else: # given all good stacks, prepare stack launch list target self.source_service.deploy.stack_launch_list = self.args[ 'stack_list'] if self.args[ 'stack_list'] else self.source_service.deploy.stack_ids except Exception as e: self.preflight_check_result.status = 'FAIL' preflight_result.append( SkyBaseValidationError( 'source stack verification: {0}'.format( simple_error_format(e)))) # attempt to read state db record try: serialized_record = skybase.actions.state.read( mode=self.mode, record_id=self.id, credentials=sky_cfg.SkyConfig.init_from_file( 'credentials').data, format='yaml') # DECISION: need general method for processing API in-flight errors if serialized_record.startswith('StateDBRecordNotFoundError'): raise StateDBRecordNotFoundError(self.id) except Exception as e: self.preflight_check_result.status = 'FAIL' preflight_result.append( SkyBaseValidationError(simple_error_format(e))) else: try: self.target_service = yaml.load(serialized_record) self.runtime.tag = self.target_service.tag except Exception as e: self.preflight_check_result.status = 'FAIL' preflight_result.append( SkyBaseValidationError('service registry init: {0}'.format( simple_error_format(e)))) else: try: bad_stacks = [] for stack in self.source_service.deploy.stack_launch_list: if stack not in self.target_service.stacks.deployed_stacks: bad_stacks.append(stack) if bad_stacks: self.preflight_check_result.status = 'FAIL' preflight_result.append( SkyBaseValidationError( 'target service {0}: stacks not deployed {1}'. format(self.target_service.service, bad_stacks))) except Exception as e: self.preflight_check_result.status = 'FAIL' preflight_result.append( SkyBaseValidationError( 'target stack verification: {0}'.format( simple_error_format(e)))) # instantiate planet based on service registry value try: self.target_planet = Planet(self.target_service.planet) except Exception as e: self.preflight_check_result.status = 'FAIL' preflight_result.append( SkyBaseValidationError('target planet init: {0}'.format( simple_error_format(e)))) else: if self.planet.planet_name != self.target_planet.planet_name: self.preflight_check_result.status = 'FAIL' preflight_result.append( 'source planet {0} not equal target planet {1}'.format( self.planet.planet_name, self.target_planet.planet_name)) # test if existing service can be updated if self.source_service and self.target_service: # TODO: test that target and source service names match! is_version_ok = ( self.source_service.manifest.get('app_version') >= self.target_service.metadata.version) if not is_version_ok: self.preflight_check_result.status = 'FAIL' preflight_result.append( SkyBaseValidationError( 'source < target service version: {0} < {1}'. format( self.source_service.manifest.get( 'app_version'), self.target_service.metadata.version))) else: self.preflight_check_result.status = 'FAIL' preflight_result.append( SkyBaseValidationError( 'cannot update service: missing (target / source): ({0} / {1})' .format(self.target_service == None, self.source_service == None))) try: # acquire salt API authtoken self.authtoken = skybase.actions.salt.get_saltapi_authtoken( runner_cfg=self.runner_cfg, planet_name=self.target_service.planet) if self.authtoken is None: self.preflight_check_result.status = 'FAIL' preflight_result.append( SkyBaseValidationError('failed to login to salt API')) except Exception as e: self.preflight_check_result.status = 'FAIL' preflight_result.append( SkyBaseValidationError( 'failed to acquire salt API authtoken: {0}'.format( simple_error_format(e)))) else: for stack in self.source_service.deploy.stack_launch_list: stack_roles = self.target_service.blueprint.get_stack_roles( stack) for role in stack_roles: stack_role_grain = self.target_service.stacks.stacks[ stack].get_stack_role_salt_grain_skybase_id(role) try: saltapi_result = skybase.actions.salt.test_ping_by_grain( grain=stack_role_grain, authtoken=self.authtoken, planet_name=self.target_service.planet, ) except Exception as e: self.preflight_check_result.status = 'FAIL' preflight_result.append( SkyBaseValidationError( 'saltapi test.ping: {0}'.format( simple_error_format(e)))) else: # verify that some minions were targeted if saltapi_result[0] != SkySaltAPI.NO_MINIONS: # verify all minions reply returned True to ping if not all(saltapi_result[0].values()): self.preflight_check_result.status = 'FAIL' preflight_result.append( SkyBaseValidationError( 'unreachable salt minions for stack-role {0}-{1} using grain {2}: {3}' .format(stack, role, stack_role_grain, saltapi_result))) else: self.preflight_check_result.status = 'FAIL' preflight_result.append( SkyBaseValidationError( '{0} using grain: {1}'.format( SkySaltAPI.NO_MINIONS, stack_role_grain))) self.preflight_check_result.set_output(preflight_result) return self.preflight_check_result