def _connect(self): url = self._get_http_ipmi_host() + self.URL_LOGIN cookies = self._get_console_cookies() headers = self._get_console_headers() data = self._get_console_data() self._session = requests.session() for count in range(5): text = self._post(url, data, cookies, headers).text parsed, retryable = self._parse_response(text) if not parsed and retryable and count < 4: log.info('Will retry after {} seconds'.format(count)) time.sleep(count) else: break if not parsed: log.error('Failed to create session') sys.exit(10) session_token = parsed[0]['SESSION_COOKIE'] if session_token == 'Failure_Session_Creation': log.error('Probably reached session limit') sys.exit(10) CSRF_token = parsed[0]['CSRFTOKEN'] self.session_token = {'SessionCookie': session_token} self.CSRF_token = {'CSRFTOKEN': CSRF_token}
def session(self) -> SessionAPI: """ Return a sessions to the MaaS server. Subsequent calls reuse the same connection """ if self._session: return self._session log.info(f'Connecting to {self.api_url}') _, self._session = SessionAPI.connect(self.api_url, apikey=self.api_key) return self._session
def diagnostics(self): jobqueue_view = 'racadm jobqueue view -i {}' output = self._ssh('racadm techsupreport collect') jid = self._find_jid(output) log.info('Sleeping for 3 minutes to collect the TSR report') time.sleep(180) view_output = self._ssh(jobqueue_view.format(jid)) self._confirm_job(view_output) output = self._ssh('racadm techsupreport export -l {}'.format( self.nfs_share)) jid = self._find_jid(output) view_output = self._ssh(jobqueue_view.format(jid)) self._confirm_job(view_output)
def ssh(self): status_command = ['chassis', 'power', 'status'] if self.parsed_args.wait: if 'off' in self._execute(status_command, output=True): log.info('Waiting for machine to turn on...') while (1): if 'off' not in self._execute(status_command, output=True): break host = self.oob_info['asset_tag'] if not host: raise OobError('Cannot perform ssh without an asset tag') call(['ssh', host])
def refresh_firmware(self): custom_fields = {} firmwares = self._get_image_info() psus = [] for firmware in firmwares: device_id = DEV_ID[firmware['DEV_TYPE']] version = firmware['CURIMG_VER'] if device_id == 'BIOS': custom_fields['BIOS'] = version elif device_id == 'TSM': custom_fields['TSM'] = version elif device_id == 'PSU': psus.append('{}/{}: {}'.format(firmware['SLOT_NO'], firmware['DEV_IDENTIFIER'], version)) custom_fields['PSU'] = ', '.join(sorted(psus)) log.info('Patching custom fields: {}'.format(custom_fields)) if not self.dcim.set_custom_fields(self.oob_info, custom_fields): log.error('Failed to refresh DCIM firmware versions')
def take_action(self, parsed_args): try: fetcher = firmware_fetchers[parsed_args.model] except KeyError as e: log.error('Unsupported device type: {}'.format(e)) sys.exit(-1) result, downloads = fetcher().get() columns = ['component', 'name', 'version', 'date'] values = [[item[col] for col in columns] for item in result] if parsed_args.download_to is None: return columns, values try: os.makedirs(parsed_args.download_to, exist_ok=True) except OSError as e: log.error('Could not create download directory: {}'.format(e)) sys.exit(-1) for url in downloads: name = url[url.rfind('/') + 1:] file_name = os.path.join(parsed_args.download_to, name) log.info('Downloading {} to {}'.format(url, file_name)) try: with open(file_name, 'wb') as fout: fout.write(urllib.request.urlopen(url).read()) except (urllib.error.URLError, OSError) as e: log.error('Failed: {}'.format(e)) if parsed_args.innoextract and name.endswith('.exe'): log.info('Extracting with innoextract') self._execute_cmd( ['innoextract', file_name, '-d', parsed_args.download_to]) return columns, values
def factory_reset(self): args = self.parsed_args if not args.force: response = input('Factory reset? [y/N] ') if response != 'y': log.info('Aborted') return self._connect() log.info('Setting preserve config') r = self._get_rpc('setpreservecfg', params={ 'PRSRV_CFG': '0,0,0,0,0,0,0,0,0,0,0,', 'PRSRV_CFG_CNT': '11', 'PRSRV_SELECT': '0,1,2,3,4,5,6,7,8,9,10,', }) log.debug(r) log.info('Starting factory reset') r = self._get_rpc('setfactorydefaults') log.debug(r) log.info('Factory reset process started') if args.wait: begin = datetime.utcnow() while datetime.utcnow() < begin + timedelta(minutes=args.timeout): try: url = self._get_http_ipmi_host() + self.URL_VALIDATE answer = self._post(url, None, self.session_token, self.CSRF_token) if answer.status_code == 200: log.info('Done.') return log.debug(answer) except (requests.exceptions.ReadTimeout, requests.exceptions.ConnectionError, ConnectionResetError, BrokenPipeError): log.info('In progress') time.sleep(10)
def firmware_upgrade_rpc(self): args = self.parsed_args handle = None if 1 in args.stages: log.info('Enter FW update mode') r = self._get_rpc('getenterfwupdatemode', params={'FWUPMODE': 1}) log.debug(r) if r and 'HANDLE' in r[0]: handle = r[0]['HANDLE'] log.info('Enter FW update mode: OK') else: log.fatal('Cannot enter FW update mode') sys.exit(-1) log.info('Update session handle: {}'.format(handle)) if 2 in args.stages: handle = handle or args.handle log.info('Rearm firmware update timer') r = self._get_rpc('rearmfwupdatetimer', params={'SESSION_ID': handle}) log.debug(r) if r[0]['NEWSESSIONID'] == handle: log.info('Rearm firmware update timer: OK') if 3 in args.stages: handle = handle or args.handle if not hasattr(self, 'CSRF_token'): self._connect() log.info('Uploading firmware bundle') ipmi = self.oob_info['ipmi'] url = ipmi + '/file_upload_firmware.html' r = requests.post( url, verify=False, cookies=self.session_token, headers=self.CSRF_token, files={'bundle?FWUPSessionid={}'.format(handle): args.bundle}) log.debug(r.status_code) if r.status_code == 200: log.info('Uploading firmware bundle: OK') if 4 in args.stages: log.info('Get Bundle Upload Status') r = self._get_rpc('getbundleupldstatus') log.debug(r) if r == []: log.info('Get Bundle Upload Status: OK') if 5 in args.stages: log.info('Validate Bundle') r = self._get_rpc('validatebundle', params={'BUNDLENAME': 'bundle_bkp.bdl'}) log.debug(r) if r[0]['STATUS'] == 0: log.info('Validate Bundle: OK') if 6 in args.stages: log.info('Replace Bundle') r = self._get_rpc('replacebundlebkp') log.debug(r) if r[0]['STATUS'] == 0: log.info('Replace Bundle: OK') if 7 in args.stages: log.info('Checking for new firmware') r = self._get_rpc('getimageinfo') log.debug(r) def has_update(x): new, cur = x['NEWIMG_VER'], x['CURIMG_VER'] try: return version_tuple(new) > version_tuple(cur) except (TypeError, ValueError): return new > cur to_update = next(filter(has_update, r), None) if to_update is None: log.info('No updates available') return else: log.info('Available update: {}'.format(to_update)) if 8 in args.stages and to_update: handle = handle or args.handle log.info('Choose component update') r = self._get_rpc('setupdatecomp', params={ 'UPDATE_FLAG': to_update['DEV_TYPE'], 'UPDATE_CNT': 1, 'FW_DEVICE_TYPE': to_update['DEV_TYPE'], 'SLOT_NO': to_update['SLOT_NO'], 'DEV_IDENTIFIER': to_update['DEV_IDENTIFIER'], 'SESSION_ID': handle, }) log.debug(r) if r == []: log.info('Choose component update: OK') log.info('Firmware upgrade process started') if 9 in args.stages: begin = datetime.utcnow() while datetime.utcnow() < begin + timedelta(minutes=args.timeout): try: r = self._get_rpc('getcompupdatestatus') log.debug(r) dev = next((x for x in r if self._matching(x, to_update)), None) log.debug(dev) progress = (dev or {}).get('UPDATE_PERCENTAGE') if progress is None: log.info('Update in progress') else: log.info('Update progress: {}%'.format(progress)) if progress == 100: log.info('Update complete!') break except (ConnectionResetError, BrokenPipeError): log.info('Update in progress') time.sleep(10) if 10 in args.stages: handle = handle or args.handle log.info('Exit FW update mode') r = self._get_rpc('getexitfwupdatemode', params={ 'MODE': 0, 'RNDNO': handle }) log.debug(r) if r == []: log.info('Exit FW update mode: OK') log.info('Done!')
def refresh_ipmi_address(self): addr = self._get_ipmi_address() custom_fields = {'IPMI': addr} log.info('Patching custom fields: {}'.format(custom_fields)) if not self.dcim.set_custom_fields(self.oob_info, custom_fields): log.error('Failed to refresh IPMI')