def _wait_session(self, session_id, timeout=1800): session_expiration = time.time() + timeout while True: resp = elcm.elcm_session_get_status(self.irmc_info, session_id) status = resp['Session']['Status'] if status == 'running' or status == 'activated': # Sleep a bit time.sleep(5) elif status == 'terminated regularly': return {} else: # Error occurred, get session log to see what happened try: session_log = elcm.elcm_session_get_log( irmc_info=self.irmc_info, session_id=session_id) except scci.SCCIClientError as e: raise scci.SCCIClientError( ('Operation Failed. Session %(session_id)s state is ' '%(session_state)s. Session log collection failed: ' '%(reason)s' % { 'session_id': session_id, 'session_state': resp['Session']['Status'], 'reason': e })) raise scci.SCCIClientError( ('Operation failed. Session %(session_id)s state is ' '%(session_state)s. Session log is: "%(session_log)s".' % { 'session_id': session_id, 'session_state': resp['Session']['Status'], 'session_log': json.dumps(session_log) })) # Check for timeout if time.time() > session_expiration: # Timeout occurred, get session log to see what happened try: session_log = elcm.elcm_session_get_log( irmc_info=self.irmc_info, session_id=session_id) except scci.SCCIClientError as e: raise elcm.ELCMSessionTimeout( 'Operation timed out. Session %(session_id)s has not ' 'finished in %(timeout)d seconds. Session log ' 'collection failed: %(reason)s' % { 'session_id': session_id, 'timeout': timeout, 'reason': e }) raise elcm.ELCMSessionTimeout( 'Operation timed out. Session %(session_id)s has not ' 'finished in %(timeout)d seconds. Session log is: ' '"%(session_log)s.' % { 'session_id': session_id, 'timeout': timeout, 'session_log': json.dumps(session_log) })
def elcm_session_delete(irmc_info, session_id, terminate=False): """send an eLCM request to remove a session from the session list :param irmc_info: node info :param session_id: session id :param terminate: a running session must be terminated before removing :raises: ELCMSessionNotFound if the session does not exist :raises: SCCIClientError if SCCI failed """ # Terminate the session first if needs to if terminate: # Get session status to check session = elcm_session_get_status(irmc_info, session_id) status = session['Session']['Status'] # Terminate session if it is activated or running if status == 'running' or status == 'activated': elcm_session_terminate(irmc_info, session_id) # Send DELETE request to the server resp = elcm_request(irmc_info, method='DELETE', path='/sessionInformation/%s/remove' % session_id) if resp.status_code == 200: return elif resp.status_code == 404: raise ELCMSessionNotFound('Session "%s" does not exist' % session_id) else: raise scci.SCCIClientError( ('Failed to remove session ' '"%(session)s" with error code %(error)s' % { 'session': session_id, 'error': resp.status_code }))
def elcm_session_get_log(irmc_info, session_id): """send an eLCM request to get session log :param irmc_info: node info :param session_id: session id :returns: dict object of session log if succeed { 'Session': { 'Id': id ... } } :raises: ELCMSessionNotFound if the session does not exist :raises: SCCIClientError if SCCI failed """ # Send GET request to the server resp = elcm_request(irmc_info, method='GET', path='/sessionInformation/%s/log' % session_id) if resp.status_code == 200: return _parse_elcm_response_body_as_json(resp) elif resp.status_code == 404: raise ELCMSessionNotFound('Session "%s" does not exist' % session_id) else: raise scci.SCCIClientError( ('Failed to get log of session ' '"%(session)s" with error code %(error)s' % { 'session': session_id, 'error': resp.status_code }))
def elcm_session_list(irmc_info): """send an eLCM request to list all sessions :param irmc_info: node info :returns: dict object of sessions if succeed { 'SessionList': { 'Contains': [ { 'Id': id1, 'Name': name1 }, { 'Id': id2, 'Name': name2 }, { 'Id': idN, 'Name': nameN }, ] } } :raises: SCCIClientError if SCCI failed """ # Send GET request to the server resp = elcm_request(irmc_info, method='GET', path='/sessionInformation/') if resp.status_code == 200: return _parse_elcm_response_body_as_json(resp) else: raise scci.SCCIClientError(('Failed to list sessions with ' 'error code %s' % resp.status_code))
def elcm_profile_delete(irmc_info, profile_name): """send an eLCM request to delete a profile :param irmc_info: node info :param profile_name: name of profile :raises: ELCMProfileNotFound if the profile does not exist :raises: SCCIClientError if SCCI failed """ # Send DELETE request to the server resp = elcm_request(irmc_info, method='DELETE', path=URL_PATH_PROFILE_MGMT + profile_name) if resp.status_code == 200: # Profile deleted return elif resp.status_code == 404: # Profile not found raise ELCMProfileNotFound('Profile "%s" not found ' 'in the profile store.' % profile_name) else: raise scci.SCCIClientError(('Failed to delete profile "%(profile)s" ' 'with error code %(error)s' % { 'profile': profile_name, 'error': resp.status_code }))
def elcm_profile_get(irmc_info, profile_name): """send an eLCM request to get profile data :param irmc_info: node info :param profile_name: name of profile :returns: dict object of profile data if succeed :raises: ELCMProfileNotFound if profile does not exist :raises: SCCIClientError if SCCI failed """ # Send GET request to the server resp = elcm_request(irmc_info, method='GET', path=URL_PATH_PROFILE_MGMT + profile_name) if resp.status_code == 200: return _parse_elcm_response_body_as_json(resp) elif resp.status_code == 404: raise ELCMProfileNotFound('Profile "%s" not found ' 'in the profile store.' % profile_name) else: raise scci.SCCIClientError(('Failed to get profile "%(profile)s" with ' 'error code %(error)s' % { 'profile': profile_name, 'error': resp.status_code }))
def elcm_profile_list(irmc_info): """send an eLCM request to list all profiles :param irmc_info: node info :returns: dict object of profiles if succeed { 'Links': { 'profileStore': [ { '@odata.id': id1 }, { '@odata.id': id2 }, { '@odata.id': idN }, ] } } :raises: SCCIClientError if SCCI failed """ # Send GET request to the server resp = elcm_request(irmc_info, method='GET', path=URL_PATH_PROFILE_MGMT) if resp.status_code == 200: return _parse_elcm_response_body_as_json(resp) else: raise scci.SCCIClientError(('Failed to list profiles with ' 'error code %s' % resp.status_code))
def elcm_profile_set(irmc_info, input_data): """send an eLCM request to set param values To apply param values, a new session is spawned with status 'running'. When values are applied or error, the session ends. :param irmc_info: node info :param input_data: param values to apply, eg. { 'Server': { 'SystemConfig': { 'BiosConfig': { '@Processing': 'execute', -- config data -- } } } } :returns: dict object of session info if succeed { 'Session': { 'Id': id 'Status': 'activated' ... } } :raises: SCCIClientError if SCCI failed """ # Prepare the data to apply if isinstance(input_data, dict): data = jsonutils.dumps(input_data) else: data = input_data # Send POST request to the server # NOTE: This task may take time, so set a timeout _irmc_info = dict(irmc_info) _irmc_info['irmc_client_timeout'] = PROFILE_SET_TIMEOUT content_type = 'application/x-www-form-urlencoded' if input_data['Server'].get('HWConfigurationIrmc'): content_type = 'application/json' resp = elcm_request(_irmc_info, method='POST', path=URL_PATH_PROFILE_MGMT + 'set', headers={'Content-type': content_type}, data=data) if resp.status_code == 202: return _parse_elcm_response_body_as_json(resp) else: raise scci.SCCIClientError(('Failed to apply param values with ' 'error code %(error)s' % { 'error': resp.status_code }))
def elcm_session_terminate(irmc_info, session_id): """send an eLCM request to terminate a session :param irmc_info: node info :param session_id: session id :raises: ELCMSessionNotFound if the session does not exist :raises: SCCIClientError if SCCI failed """ # Send DELETE request to the server resp = elcm_request(irmc_info, method='DELETE', path='/sessionInformation/%s/terminate' % session_id) if resp.status_code == 200: return elif resp.status_code == 404: raise ELCMSessionNotFound('Session "%s" does not exist' % session_id) else: raise scci.SCCIClientError(('Failed to terminate session ' '"%(session)s" with error code %(error)s' % {'session': session_id, 'error': resp.status_code}))
def elcm_profile_create(irmc_info, param_path): """send an eLCM request to create profile To create a profile, a new session is spawned with status 'running'. When profile is created completely, the session ends. :param irmc_info: node info :param param_path: path of profile :returns: dict object of session info if succeed { 'Session': { 'Id': id 'Status': 'activated' ... } } :raises: SCCIClientError if SCCI failed """ # Send POST request to the server # NOTE: This task may take time, so set a timeout _irmc_info = dict(irmc_info) _irmc_info['irmc_client_timeout'] = PROFILE_CREATE_TIMEOUT resp = elcm_request(_irmc_info, method='POST', path=URL_PATH_PROFILE_MGMT + 'get', params={'PARAM_PATH': param_path}) if resp.status_code == 202: return _parse_elcm_response_body_as_json(resp) else: raise scci.SCCIClientError(('Failed to create profile for path ' '"%(param_path)s" with error code ' '%(error)s' % { 'param_path': param_path, 'error': resp.status_code }))
def _process_session_data(irmc_info, operation, session_id, session_timeout=BIOS_CONFIG_SESSION_TIMEOUT): """process session for Bios config backup/restore or RAID config operation :param irmc_info: node info :param operation: one of 'BACKUP_BIOS', 'RESTORE_BIOS' or 'CONFIG_RAID' :param session_id: session id :param session_timeout: session timeout :return: a dict with following values: { 'bios_config': <data in case of BACKUP/RESTORE_BIOS operation>, 'warning': <warning message if there is> } or { 'raid_config': <data of raid adapter profile>, 'warning': <warning message if there is> } """ session_expiration = time.time() + session_timeout while time.time() < session_expiration: # Get session status to check session = elcm_session_get_status(irmc_info=irmc_info, session_id=session_id) status = session['Session']['Status'] if status == 'running' or status == 'activated': # Sleep a bit time.sleep(5) elif status == 'terminated regularly': result = {} if operation == 'BACKUP_BIOS': # Bios profile is created, get the data now result['bios_config'] = elcm_profile_get( irmc_info=irmc_info, profile_name=PROFILE_BIOS_CONFIG) elif operation == 'RESTORE_BIOS': # Bios config applied successfully pass elif operation == 'CONFIG_RAID': # Getting raid config result['raid_config'] = elcm_profile_get( irmc_info, PROFILE_RAID_CONFIG) # Cleanup operation by deleting related session and profile. # In case of error, report it as warning instead of error. try: elcm_session_delete(irmc_info=irmc_info, session_id=session_id, terminate=True) if operation == 'CONFIG_RAID': return result elcm_profile_delete(irmc_info=irmc_info, profile_name=PROFILE_BIOS_CONFIG) except scci.SCCIError as e: result['warning'] = e return result else: # Error occurred, get session log to see what happened session_log = elcm_session_get_log(irmc_info=irmc_info, session_id=session_id) raise scci.SCCIClientError( ('Failed to %(operation)s config. ' 'Session log is "%(session_log)s".' % { 'operation': operation, 'session_log': jsonutils.dumps(session_log) })) else: raise ELCMSessionTimeout(('Failed to %(operation)s config. ' 'Session %(session_id)s log is timeout.' % { 'operation': operation, 'session_id': session_id }))
def elcm_request(irmc_info, method, path, **kwargs): """send an eLCM request to the server :param irmc_info: dict of iRMC params to access the server node { 'irmc_address': host, 'irmc_username': user_id, 'irmc_password': password, 'irmc_port': 80 or 443, default is 443, 'irmc_auth_method': 'basic' or 'digest', default is 'basic', 'irmc_client_timeout': timeout, default is 60, ... } :param method: request method such as 'GET', 'POST' :param path: url path for eLCM request :returns: requests.Response from SCCI server :raises SCCIInvalidInputError: if port and/or auth_method params are invalid :raises SCCIClientError: if SCCI failed """ host = irmc_info['irmc_address'] port = irmc_info.get('irmc_port', 443) auth_method = irmc_info.get('irmc_auth_method', 'basic') userid = irmc_info['irmc_username'] password = irmc_info['irmc_password'] client_timeout = irmc_info.get('irmc_client_timeout', 60) # Request headers, params, and data headers = kwargs.get('headers', {'Accept': 'application/json'}) params = kwargs.get('params') data = kwargs.get('data') auth_obj = None try: protocol = {80: 'http', 443: 'https'}[port] auth_obj = { 'basic': requests.auth.HTTPBasicAuth(userid, password), 'digest': requests.auth.HTTPDigestAuth(userid, password) }[auth_method.lower()] except KeyError: raise scci.SCCIInvalidInputError( ("Invalid port %(port)d or " + "auth_method for method %(auth_method)s") % { 'port': port, 'auth_method': auth_method }) try: r = requests.request(method, protocol + '://' + host + path, headers=headers, params=params, data=data, verify=False, timeout=client_timeout, allow_redirects=False, auth=auth_obj) except requests.exceptions.RequestException as requests_exception: raise scci.SCCIClientError(requests_exception) # Process status_code 401 if r.status_code == 401: raise scci.SCCIClientError('UNAUTHORIZED') return r