def _parse_array_spec(self, arrayspec): controller = None if arrayspec.disks: for disk in list(arrayspec.disks) + list(arrayspec.hotspares): if controller is None: controller = disk.id[0] if controller != disk.id[0]: raise pygexc.UnsupportedFunctionality( 'Cannot span arrays across controllers') raidmap = self._raid_number_map(controller) if not raidmap: raise pygexc.InvalidParameterValue( 'There are no available drives for a new array') requestedlevel = str(arrayspec.raid).lower() if requestedlevel not in raidmap: raise pygexc.InvalidParameterValue( 'Requested RAID "{0}" not available on this ' 'system with currently available drives'.format( requestedlevel)) rdinfo = raidmap[str(arrayspec.raid).lower()] rdlvl = str(rdinfo[0]) defspan = 1 if rdinfo[1] == 1 else 2 spancount = defspan if arrayspec.spans is None else arrayspec.spans drivesperspan = str(len(arrayspec.disks) // int(spancount)) hotspares = arrayspec.hotspares drives = arrayspec.disks if hotspares: hstr = '|'.join([str(x.id[1]) for x in hotspares]) + '|' else: hstr = '' drvstr = '|'.join([str(x.id[1]) for x in drives]) + '|' pth = '/api/function/raid_conf?params=raidlink_CheckConfisValid' args = [ pth, controller, rdlvl, spancount, drivesperspan, drvstr, hstr ] url = ','.join([str(x) for x in args]) rsp = self.wc.grab_json_response(url) if rsp['items'][0]['errcode'] == 16: raise pygexc.InvalidParameterValue('Incorrect number of disks') elif rsp['items'][0]['errcode'] != 0: raise pygexc.InvalidParameterValue( 'Invalid configuration: {0}'.format( rsp['items'][0]['errcode'])) return { 'capacity': rsp['items'][0]['freeCapacity'], 'controller': controller, 'drives': drvstr, 'hotspares': hstr, 'raidlevel': rdlvl, 'spans': spancount, 'perspan': drivesperspan, } else: pass # TODO: adding new volume to existing array would be here
def set_ntp_server(self, server, index=0): if self.has_tsm: if not (0 <= index <= 1): raise pygexc.InvalidParameterValue("Index must be 0 or 1") cmddata = bytearray((1 + index, )) cmddata += server.ljust(128, '\x00') self.ipmicmd.xraw_command(netfn=0x32, command=0xa8, data=cmddata) return True elif self.is_fpc: if not 0 <= index <= 2: raise pygexc.InvalidParameterValue( 'SMM supports indexes 0 through 2') self.smmhandler.set_ntp_server(server, index) return True return None
def decode_pet(self, specifictrap, petdata): if isinstance(specifictrap, int): specifictrap = struct.unpack('4B', struct.pack('>I', specifictrap)) if len(specifictrap) != 4: raise pygexc.InvalidParameterValue( 'specifictrap should be integer number or 4 byte array') specifictrap = bytearray(specifictrap) sensor_type = specifictrap[1] event_type = specifictrap[2] # Event Offset is in first event data byte, so no need to fetch it here # evtoffset = specifictrap[3] & 0b1111 deassertion = (specifictrap[3] & 0b10000000) == 0b10000000 # alertseverity = petdata[26] sensorid = petdata[28] event_data = petdata[31:34] event = {} seqnum = struct.unpack_from('>H', buffer(petdata[16:18]))[0] ltimestamp = struct.unpack_from('>I', buffer(petdata[18:22]))[0] petack = bytearray( struct.pack('<HIBBBBBB', seqnum, ltimestamp, petdata[25], petdata[27], sensorid, *event_data)) try: self._ipmicmd.xraw_command(netfn=4, command=0x17, data=petack) except pygexc.IpmiException: # Ignore failure to ack for now pass self._populate_event(deassertion, event, event_data, event_type, sensor_type, sensorid) event['timecode'] = ltimestamp _fix_sel_time((event, ), self._ipmicmd) return event
def set_power(self, powerstate, wait=False): if powerstate == 'boot': oldpowerstate = self.get_power()['powerstate'] powerstate = 'on' if oldpowerstate == 'off' else 'reset' reqpowerstate = powerstate if powerstate not in powerstates: raise exc.InvalidParameterValue( "Unknown power state %s requested" % powerstate) powerstate = powerstates[powerstate] result = self.wc.grab_json_response_with_status( self.powerurl, {'ResetType': powerstate}) if result[1] < 200 or result[1] >= 300: raise exc.PyghmiException(result[0]) if wait and reqpowerstate in ('on', 'off', 'softoff', 'shutdown'): if reqpowerstate in ('softoff', 'shutdown'): reqpowerstate = 'off' timeout = os.times()[4] + 300 while (self.get_power()['powerstate'] != reqpowerstate and os.times()[4] < timeout): time.sleep(1) if self.get_power()['powerstate'] != reqpowerstate: raise exc.PyghmiException( "System did not accomplish power state change") return {'powerstate': reqpowerstate} return {'pendingpowerstate': reqpowerstate}
def set_power(self, powerstate, wait=False): """Request power state change :param powerstate: * on -- Request system turn on * off -- Request system turn off without waiting for OS to shutdown * shutdown -- Have system request OS proper shutdown * reset -- Request system reset without waiting for OS * boot -- If system is off, then 'on', else 'reset' :param wait: If True, do not return until system actually completes requested state change for 300 seconds. If a non-zero number, adjust the wait time to the requested number of seconds :returns: dict -- A dict describing the response retrieved """ if powerstate not in power_states: raise exc.InvalidParameterValue( "Unknown power state %s requested" % powerstate) self.newpowerstate = powerstate response = self.ipmi_session.raw_command(netfn=0, command=1) if 'error' in response: raise exc.IpmiException(response['error']) self.powerstate = 'on' if (response['data'][0] & 1) else 'off' if self.powerstate == self.newpowerstate: return {'powerstate': self.powerstate} if self.newpowerstate == 'boot': self.newpowerstate = 'on' if self.powerstate == 'off' else 'reset' response = self.ipmi_session.raw_command( netfn=0, command=2, data=[power_states[self.newpowerstate]]) if 'error' in response: raise exc.IpmiException(response['error']) self.lastresponse = {'pendingpowerstate': self.newpowerstate} waitattempts = 300 if not isinstance(wait, bool): waitattempts = wait if (wait and self.newpowerstate in ('on', 'off', 'shutdown', 'softoff')): if self.newpowerstate in ('softoff', 'shutdown'): self.waitpowerstate = 'off' else: self.waitpowerstate = self.newpowerstate currpowerstate = None while currpowerstate != self.waitpowerstate and waitattempts > 0: response = self.ipmi_session.raw_command(netfn=0, command=1, delay_xmit=1) if 'error' in response: return response currpowerstate = 'on' if (response['data'][0] & 1) else 'off' waitattempts -= 1 if currpowerstate != self.waitpowerstate: raise exc.IpmiException( "System did not accomplish power state change") return {'powerstate': currpowerstate} else: return self.lastresponse
def _get_status(self, disk, realcfg): for cfgdisk in realcfg.disks: if disk.id == cfgdisk.id: currstatus = cfgdisk.status break else: raise pygexc.InvalidParameterValue('Requested disk not found') return currstatus
def set_ntp_server(self, server, index=0): if self.has_tsm: if not (0 <= index <= 1): raise pygexc.InvalidParameterValue("Index must be 0 or 1") cmddata = bytearray((1 + index, )) cmddata += server.ljust(128, '\x00') self.ipmicmd.xraw_command(netfn=0x32, command=0xa8, data=cmddata) return True return None
def set_bootdev(self, bootdev, persist=False, uefiboot=None): """Set boot device to use on next reboot :param bootdev: *network -- Request network boot *hd -- Boot from hard drive *safe -- Boot from hard drive, requesting 'safe mode' *optical -- boot from CD/DVD/BD drive *setup -- Boot into setup utility *default -- remove any directed boot device request :param persist: If true, ask that system firmware use this device beyond next boot. Be aware many systems do not honor this :param uefiboot: If true, request UEFI boot explicitly. If False, request BIOS style boot. None (default) does not modify the boot mode. :raises: PyghmiException on an error. :returns: dict or True -- If callback is not provided, the response """ reqbootdev = bootdev if (bootdev not in boot_devices_write and bootdev not in boot_devices_read): raise exc.InvalidParameterValue('Unsupported device ' + repr(bootdev)) bootdev = boot_devices_write.get(bootdev, bootdev) if bootdev == 'None': payload = {'Boot': {'BootSourceOverrideEnabled': 'Disabled'}} else: payload = { 'Boot': { 'BootSourceOverrideEnabled': 'Continuous' if persist else 'Once', 'BootSourceOverrideTarget': bootdev, } } if uefiboot is not None: uefiboot = 'UEFI' if uefiboot else 'Legacy' payload['BootSourceOverrideMode'] = uefiboot self._do_web_request(self.sysurl, payload, method='PATCH') return {'bootdev': reqbootdev}
def set_fw_options(self, options): changes = False random.seed() ident = 'ASU-%x-%x-%x-0' % (random.getrandbits(48), random.getrandbits(32), random.getrandbits(64)) configurations = etree.Element('configurations', ID=ident, type='update', update='ASU Client') for option in options.keys(): if options[option]['new_value'] is None: continue if options[option]['current'] == options[option]['new_value']: continue if options[option]['pending'] == options[option]['new_value']: continue if options[option]['readonly']: errstr = '{0} is read only'.format(option) if options[option]['readonly_why']: ea = ' due to one of the following settings: {0}'.format( ','.join(sorted(options[option]['readonly_why']))) errstr += ea raise pygexc.InvalidParameterValue(errstr) if (isinstance(options[option]['new_value'], str) or isinstance(options[option]['new_value'], unicode)): # Coerce a simple string parameter to the expected list format options[option]['new_value'] = [options[option]['new_value']] options[option]['pending'] = options[option]['new_value'] is_list = options[option]['is_list'] count = 0 changes = True config = etree.Element('config', ID=options[option]['lenovo_id']) configurations.append(config) group = etree.Element('group', ID=options[option]['lenovo_group']) config.append(group) setting = etree.Element('setting', ID=options[option]['lenovo_setting']) group.append(setting) if is_list: container = etree.Element('list_data') setting.append(container) else: container = etree.Element('enumerate_data') setting.append(container) for value in options[option]['new_value']: choice = etree.Element('choice') container.append(choice) label = etree.Element('label') label.text = value choice.append(label) if is_list: count += 1 instance = etree.Element( 'instance', ID=options[option]['lenovo_instance'], order=str(count)) else: instance = etree.Element( 'instance', ID=options[option]['lenovo_instance']) choice.append(instance) if not changes: return xml = etree.tostring(configurations) data = EfiCompressor.FrameworkCompress(xml, len(xml)) filehandle = self.imm_open("asu_update.efi", write=True, size=len(data)) self.imm_write(filehandle, len(data), data) self.imm_close(filehandle)
def _create_array(self, pool): params = self._parse_array_spec(pool) url = '/api/function/raid_conf?params=raidlink_GetDefaultVolProp' args = (url, params['controller'], 0, params['drives']) props = self.wc.grab_json_response(','.join([str(x) for x in args])) props = props['items'][0] volumes = pool.volumes remainingcap = params['capacity'] nameappend = 1 vols = [] currvolnames = None currcfg = None for vol in volumes: if vol.name is None: # need to iterate while there exists a volume of that name if currvolnames is None: currcfg = self.get_storage_configuration() currvolnames = set([]) for pool in currcfg.arrays: for volume in pool.volumes: currvolnames.add(volume.name) name = props['name'] + '_{0}'.format(nameappend) nameappend += 1 while name in currvolnames: name = props['name'] + '_{0}'.format(nameappend) nameappend += 1 else: name = vol.name stripesize = props['stripsize'] if vol.stripesize is None \ else vol.stripesize strsize = 'remainder' if vol.size is None else str(vol.size) if strsize in ('all', '100%'): volsize = params['capacity'] elif strsize in ('remainder', 'rest'): volsize = remainingcap elif strsize.endswith('%'): volsize = int(params['capacity'] * float(strsize.replace('%', '')) / 100.0) else: try: volsize = int(strsize) except ValueError: raise pygexc.InvalidParameterValue('Unrecognized size ' + strsize) remainingcap -= volsize if remainingcap < 0: raise pygexc.InvalidParameterValue( 'Requested sizes exceed available capacity') vols.append('{0};{1};{2};{3};{4};{5};{6};{7};{8};|'.format( name, volsize, stripesize, props['cpwb'], props['cpra'], props['cpio'], props['ap'], props['dcp'], props['initstate'])) url = '/api/function' arglist = '{0},{1},{2},{3},{4},{5},'.format( params['controller'], params['raidlevel'], params['spans'], params['perspan'], params['drives'], params['hotspares']) arglist += ''.join(vols) parms = {'raidlink_AddNewVolWithNaAsync': arglist} rsp = self.wc.grab_json_response(url, parms) if rsp['return'] != 0: raise Exception('Unexpected response to add volume command: ' + repr(rsp)) self._wait_storage_async()