def adjustCommandsToParted(commands): """ Adjusts commands to use 'parted' tool expected inputs. @type commands: list @param commands: Conventional Disk operations to be performed @rtype: list @returns: disk operations using parted command expected input """ # report operation llecho('Adjusting conventional disk operations to be used by parted command') # update commands for command in commands: # update disk name to use full path if command['mpath_master'] is not None: command['disk_name'] = MPATH_PATH % command else: command['disk_name'] = DEVICE_PATH % command # command has partition type: update it if 'type' in command: command['type'] = TYPE[command['type']]
def stop(): """ Stops all RAID arrays so that all resources (partitions) are released and disks partitioning can be done FIXME: There is a know bug (73870) that happens when installing an automatic partitioning scheme after a previous RAID parition in multiple disks. It was added a loop with a sleep trying to stop until 10 times if it fails. A better solution should be investigated here. @rtype: None @returns: nothing """ # report operation llecho('Stopping all RAID arrays') # stop the raid device status = 0 for i in range(0, 10): # run the command to stop raid status = run(CMD_STOP_MDADM) # raid stopped successfully: quit if status == 0: break # wait 1 second before another try time.sleep(1) # problems to stop raid: exit -1 if status != 0: llecho('Error: cannot stop RAID arrays') raise PKVMError('PARTITIONER', 'RAID', 'STOP_SWRAID')
def start(): """ Activate all existing RAID arrays so that RAID operations can be performed @rtype: None @returns: nothing """ # report operation llecho('Activating all existing RAID arrays') # failed creating the array: exit status = run(CMD_START_MDADM) if status != 0 and status != 1: llecho('Error: cannot activate RAID arrays') sys.exit(1)
def deleteArrays(commands): """ Processes RAID operations as specified by the passed list of commands and performs the ones which delete RAID arrays @type commands: list @param commands: list of commands to be processed @rtype: None @returns: nothing """ # report operation llecho('Deleting RAID arrays') # delete raid arrays for cmd in commands: if cmd['command'] == 'delete:raid': _delete(cmd)
def _fixDiskLabel(device): """ Fixes disk label so it can be properly partitioned afterwards. @type device: basestring @param device: passed device (e.g. '/dev/sda', '/dev/sdb', ...) @rtype: None @returns: Nothing """ # disk label could not be fixed: report error status = run(CMD_FIX_DISK_LABEL % {'disk': device}) if status != 0: llecho(ERROR_FIX % device) sys.exit(1) # partition table was reset: report operation llecho('Partition table for %s was reset' % device)
def loadCommands(path): """ Loads from the pickle file at the passed path conventional disk operations to be performed @type path: basestring @param path: path of the pickle file to be loaded @rtype: dict @returns: Conventional Disk operations to be performed """ # report operation llecho('Loading Conventional disk operations to be performed') # cannot locate list of commands to be processed: report and exit if os.path.exists(path) == False: llecho('Error: cannot locate list of operations to be done') sys.exit(1) # load the list of commands try: stream = open(path) commands = cPickle.load(stream) stream.close() # loading error: report and exit except (EnvironmentError, cPickle.PickleError): llecho('Error: cannot load list of operations to be done') sys.exit(1) # adjust commands to be used by parted adjustCommandsToParted(commands) # return loaded commands return commands
def loadCommands(path): """ Loads from the pickle file at the passed path RAID operations to be performed @type path: basestring @param path: path of the pickle file to be loaded @rtype: dict @returns: RAID operations to be performed """ # report operation llecho('Loading RAID operations to be performed') # cannot locate list of commands to be processed: report and exit if os.path.exists(path) == False: llecho('Error: cannot locate list of operations to be done') sys.exit(1) # load the list of commands try: stream = open(path) commands = cPickle.load(stream) stream.close() # loading error: report and exit except: llecho('Error: cannot load list of operations to be done') sys.exit(1) return commands
def _setPartType(device): """ Sets the partition type of the passed device as linux_raid_auto @type devices: basestring @param devices: device whose partition type is to be set @rtype: None @returns: nothing """ # report the operation llecho('Setting the partition type of %s as linux_raid_auto' % device) # not a valid device name: error match = PATTERN_PART.match(device) if match == None: llecho('Error: cannot parse %s into a device name ' 'and partition number' % device) sys.exit(1) # get device name and partition number info = match.groupdict() # cannot set partition type: error status = run(CMD_SET_PART_TYPE % info) if status != 0: llecho('Error: cannot set the partition type of ' '/dev/%(device)s Id %(number)s' % info) sys.exit(1)
def _delete(cmd): """ Deletes a RAID array as specified in the passed command @type cmd: dict @param cmd: command to be performed @rtype: None @returns: nothing """ # report the operation llecho('Deleting RAID array /dev/%(name)s' % cmd) # umount the raid partition run(CMD_UMOUNT_RAID % cmd) # failed stopping the array: report and exit status = run(CMD_STOP_RAID % cmd) if status != 0: llecho('Error: cannot stop the array') sys.exit(1) # failed zeroing the superblock of the raid devices: report and exit status = run(CMD_ZERO_RAID % { 'devices': ' '.join(['/dev/%s' % d for d in cmd['devices']]), }) if status != 0: llecho('Error: cannot zero the superblocks of the RAID devices') sys.exit(1)
def _runPartedCommand(partedCommand, disk, errorMessage, hasMultipath=False, tolerant=False): """ Runs a parted command and re-read disk partition table. @type partedCommand: basestring @param partedCommand: parted command to run @type disk: basestring @param disk: disk path @type errorMessage: basestring @param errorMessage: error message to user in case of a failure @type hasMultipath: bool @param hasMultipath: flag that informs if system has multipath @type tolerant: bool @param tolerant: True to not exit if error found, False otherwise @rtype: None @returns: nothing """ # runs command (status, output) = getstatusoutput(partedCommand) # log command line llecho("Running: %s" % partedCommand) # log exit status and output llecho("Status: %d" % status) llecho("Output:\n%s\n" % output) if hasMultipath: return # FIXME: use a safer check here, i.e., a regular expression to match the # desired output. # partition table needs to be re-read: do it if 're-read' in output: # partition table was successfully re-read: change status accordingly if _reReadPartitionTable(disk, hasMultipath): status = 0 # command failed: log and exit if status != 0: llecho(errorMessage) if not tolerant: sys.exit(1)
def deletePartitions(diskCommands, hasMultipath, tolerant=False): """ Deletes all partitions. @type diskCommands: dict @param diskCommands: abstract commands for conventional disks @type hasMultipath: bool @param hasMultipath: info about multipath on machine @type tolerant: bool @param tolerant: True to not exit if error found, False otherwise @rtype: None @returns: Nothing """ # report the operation llecho('Deleting Partitions') # reverse order the list of deletion commands. this is necessary because # the parted tool reorder the logical partitions when they are removed in # order. for example, if you remove logical partition 5, 6 will become 5, 7 # will become 6 and so on. deleteCommands = _reverseOrderDeleteCommands(diskCommands) # delete partitions for cmd in deleteCommands: # report operation and run it llecho('Deleting partition /dev/%(name)s' % cmd) # get parameters to delete partition partedCommand = CMD_DELETE_PARTITION % cmd disk = cmd['disk_name'] errorMessage = ERROR_DELETE % cmd # delete partition _runPartedCommand(partedCommand, disk, errorMessage, hasMultipath, tolerant)
def _reReadPartitionTable(disk, hasMultipath = False): """ Asks the kernel to re-read the partition table before trying to format it @type disk: basestring @param disk: disk device name @type hasMultipath: bool @param hasMultipath: info about multipath on machine @rtype: bool @return: True if partition table sync successfull. False otherwise """ # give some opportunities to sync the partition table before # returning false for i in range(1, MAX_SYNC_ATTEMPTS): # log the number of attempts llecho('Re-reading partition table for %s (try %d)' % (disk, i)) # wait 1 second time.sleep(1) # FIXME: during the attempt to read the partitions, raid should # be inactive or it will block devices belonging to its array # and will make the next command to fail. It is not clear why # raid becomes active here since is has been stopped in # manage_parts. It demands further investigation. if hasMultipath: raid.stop() # partition table re-read successfully: return success if run(CMD_HDPARM_Z % disk) == 0: return True return False
def createPartitions(diskCommands, hasMultipath, sector_size): """ Creates all partitions. @type diskCommands: dict @param diskCommands: abstract commands for conventional disks @type hasMultipath: bool @param hasMultipath: info about multipath on machine @rtype: None @returns: Nothing """ # report the operation llecho('Creating Partitions') # create partitions for cmd in diskCommands: # command is not create: do nothing if cmd['command'] != 'create:partition': continue # remove any possible LVM garbage from the PVs partition llecho('Clear partitions before creating') run('dd if=/dev/zero of=%s bs=%d seek=%s count=2048' % (cmd['disk_name'], sector_size, cmd['start'])) # command is create: report operation and run it llecho('Creating partition /dev/%(name)s' % cmd) # get parameters to create partition cmd['type'] = TYPE[cmd['type']] partedCommand = CMD_CREATE_PARTITION % cmd disk = cmd['disk_name'] errorMessage = ERROR_CREATE % cmd # create partition _runPartedCommand(partedCommand, disk, errorMessage, hasMultipath) # partition is PReP: set as bootable if cmd['fs'] == 'prep': _setFlag(cmd, 'boot', hasMultipath) # partition is PReP, RAID or LVM: set respective flag if cmd['fs'] in ['prep', 'raid', 'lvm']: _setFlag(cmd, cmd['fs'], hasMultipath) if cmd['fs'] == 'extended': continue
def _create(cmd): """ Creates a RAID array as specified in the passed command @type cmd: dict @param cmd: command to be performed @rtype: None @returns: nothing """ # report the operation llecho('Creating RAID level %(level)d array /dev/%(name)s from %(devices)s' % cmd) # FIXME: stop raid before any attempt to create an array to assure # that there will not be any blocked device causing error stop() # build command line to be used to create the array cmdLine = CMD_CREATE_RAID[cmd['level']] % { 'name': cmd['name'], 'level': cmd['level'], 'chunkSize': cmd['chunkSize'], 'nDevices': len(cmd['devices']) - cmd['spares'], 'nSpares': cmd['spares'], 'devices': ' '.join(['/dev/%s' % d for d in cmd['devices']]), 'metadata': cmd['metadata'], } # failed creating the array: exit status = run(cmdLine) if status != 0: llecho('Error: cannot create the array') sys.exit(1) # FIXME: start raid again after the command was performed # successfully start() # set the partition type of each device as FD for device in cmd['devices']: _setPartType(device) # reiserfs type not chosen on a RAID 0: make a filesystem on this array if cmd['fileSystem'] not in ['reiserfs', 'swap']: llecho('Creating filesystem of type %(fileSystem)s on /dev/%(name)s - RAID %(level)d' % cmd) run('mkfs\.%(fileSystem)s /dev/%(name)s' % cmd)
def wait(commands): """ Processes RAID operations as specified by the passed list of commands and waits for the RAID arrays set to be created and reused to become clean. An array is not clean when, for example, it has just been created and its spares are synching. @type commands: list @param commands: list of commands to be processed @rtype: None @returns: nothing """ # report operation llecho('Waiting for RAID arrays to become clean') # get RAID arrays to be monitored mds = [] for cmd in commands: # created or reused array: monitor it if cmd['command'] in ('create:raid', 'use:raid'): # level 0 array: no need to monitor if cmd['level'] != 0: mds.append(cmd['name']) # show arrays llecho('Arrays: %s' % mds) # open file objects for each array files = {} for md in mds: files[md] = _openFiles(md) # get descriptors for the files to be monitored mdsByFd = {} status = {} names = {} for md, fobjs in files.iteritems(): fd = fobjs['completed'] mdsByFd[fd] = fobjs status[fd] = {} names[fd] = md fds = mdsByFd.keys() # monitor arrays until all are clean while True: r = _poll(fds, interval = 0.5, timeout = 1.0) # read files for the mds with data available for fd in r: _readFiles(mdsByFd[fd], status[fd]) # md finished sync: stop monitoring it if status[fd]['recovery'] == 'idle': llecho('Array /dev/%s is clean' % names[fd]) fds.remove(fd) # no more mds to be monitored: done if len(fds) == 0: break