diff --git a/dialog/di_data.py b/dialog/di_data.py new file mode 100644 index 0000000..2302f6d --- /dev/null +++ b/dialog/di_data.py @@ -0,0 +1,708 @@ +#!/usr/bin/python + +import re +import commands +from xml.dom import minidom +from xml.dom.ext import PrettyPrint + +# xml file names +install_xml = '/var/install/test.xml' +config_xml = '../xml/config.xml' + +def to_xml_attr(doc, node, cls, name): + ''' This method is called by to_xml. Its function is to create an attribute, and set its value based on xml data member + doc - xml document + node - xml node + cls - python class/python instance + name - attribute name''' + attr = doc.createAttribute(name) + attr.value = getattr(cls, name) + node.setAttributeNode(attr) + +class SerialNumber: + ''' serial number ''' + @staticmethod + def to_xml(doc, p_node): + ''' write SerialNumber into xml + doc - xml document instance + p_node - xml node (parent node)''' + sn = doc.createElement('serial-number') + data = doc.createTextNode('12345') + sn.appendChild(data) + p_node.appendChild(sn) + + def out_xml(doc): + '''delect serialnumber from xml''' + doc.removeChild('serial-number') + +class Partition: + ''' disk partition ''' + unit='' + dict={} + def __init__(self, dev, st, sz, tp, fs, fg, fr): + ''' Partition init function ''' + self.device = dev + self.start = st + self.size = sz + self.type = tp + self.filesystem = fs + self.flags = fg + self.from_os = fr + Partition.dict[dev] = self + + @staticmethod + def init_from_os(): + ''' create a Partition instance from hardware info''' + Partition.unit='GB' + cmd_cat = 'cat /proc/partitions' + + st,o = commands.getstatusoutput(cmd_cat) + if st: + print "Error cat : command not found or /proc/partitions dosen't exsit" + return + + # an example of `cat /proc/partitions` + # major minor #blocks name + # + # 8 0 488386584 sda + # 8 1 97659103 sda1 + # 8 2 1951897 sda2 + # 8 3 97659135 sda3 + # 8 4 291113865 sda4 + # 8 16 244197527 sdb + # 8 17 62918541 sdb1 + # 8 18 4200997 sdb2 + # 8 19 1 sdb3 + # 8 21 1238016 sdb5 + # 9 0 803136 md0 + + l = [ s.split()[-1] for s in o.splitlines() if re.match(r'\s*\d+', s)] + # devices : 1) sda, sdb(first if ), or md0 (second if) + for d in [ i for i in l if not re.search(r'\d+', i) + or re.search(r'([a-zA-Z]+)', i).group(1) not in l]: + st,o=commands.getstatusoutput('LANG=c parted /dev/%s unit %s print'%(d,Partition.unit)) + if st: + # if a raid device wasn't formated, exec "parted it unit GB print" + # command will return a error info. + continue + # an example of o's content + # Model: ATA Hitachi HDS72105 (scsi) + # Disk /dev/sda: 500GB + # Sector size (logical/physical): 512B/512B + # Partition Table: msdos + # + # Number Start End Size Type File system Flags + # 1 0.00GB 100GB 100GB primary ext3 boot + # 2 100GB 102GB 2.00GB primary linux-swap(v1) + # 3 102GB 202GB 100GB primary ext3 + # 4 202GB 500GB 298GB primary ext3 + + located = False + for s in o.splitlines(): + # Only match number, start, end, and size, that's enough. + # And when in raid case, no type appears. + title_p = re.compile('Number\s+Start\s+End\s+Size\s') + if title_p.match(s): + nm_i = s.find('Number') + st_i = s.find('Start') + end_i = s.find('End') + sz_i = s.find('Size') + tp_i = s.find('Type') + fs_i = s.find('File system') + flg_i= s.find('Flags') + if tp_i < 0: tp_i=fs_i + located = True + elif located: + Partition(d[:2] == 'md' and d or d+s[nm_i:st_i].strip(), # device name + s[st_i:end_i].strip().rstrip(Partition.unit), # start + s[sz_i:tp_i].strip().rstrip(Partition.unit), # size + s[tp_i:fs_i].strip(), # type + re.search('swap', s[fs_i:flg_i]) and 'swap' or s[fs_i:flg_i].strip(), # file system + s[flg_i:].strip(), # flags + 'yes' + ) + + @staticmethod + def init_from_xml(node): + ''' create Partition instances from xml node ''' + Partition.unit = node.attributes['unit'].value.encode('ascii') + for p in node.childNodes: + if p.nodeType == node.ELEMENT_NODE and p.nodeName == 'partition': + Partition(p.attributes['device'].value.encode('ascii'),\ + p.attributes['start'].value.encode('ascii'),\ + p.attributes['size'].value.encode('ascii'),\ + p.attributes['type'].value.encode('ascii'),\ + p.attributes['file-system'].value.encode('ascii'),\ + p.attributes['flags'].value.encode('ascii'),\ + p.attributes['from_os'].value.encode('ascii')) + + + @staticmethod + def to_xml(doc, p_node): + ''' write all Partition instance into xml +doc - xml document instance +p_node - xml node (parent node)''' + pts = doc.createElement("partitions") + unit_attr = doc.createAttribute('unit') + unit_attr.value = Partition.unit + pts.setAttributeNode(unit_attr) + P_keys = Partition.dict.keys() + P_keys.sort() + for k in P_keys: + p = Partition.dict[k] + pt = doc.createElement('partition') + dev_attr = doc.createAttribute('device') + start_attr = doc.createAttribute('start') + size_attr = doc.createAttribute('size') + type_attr = doc.createAttribute('type') + fs_attr = doc.createAttribute('file-system') + flags_attr = doc.createAttribute('flags') + from_attr = doc.createAttribute('from_os') + dev_attr.value = p.device + start_attr.value = p.start + size_attr.value = p.size + type_attr.value = p.type + fs_attr.value = p.filesystem + flags_attr.value = p.flags + from_attr.value = p.from_os + pt.setAttributeNode(dev_attr) + pt.setAttributeNode(start_attr) + pt.setAttributeNode(size_attr) + pt.setAttributeNode(type_attr) + pt.setAttributeNode(fs_attr) + pt.setAttributeNode(flags_attr) + pt.setAttributeNode(from_attr) + pts.appendChild(pt) + p_node.appendChild(pts) + + @staticmethod + def get_size(dev): + try: + return Partition.dict[dev].size + except: + return None + +class Raid: + ''' raid information ''' + dict = {} + def __init__(self, dev, from_os, level, a_devs, s_devs=[]): + ''' Raid init function ''' + self.device = dev + self.from_os = from_os + self.level = level + self.active_components = a_devs + self.spare_components = s_devs + Raid.dict[dev] = self + + @staticmethod + def init_from_os(): + cmd = 'cat /proc/mdstat' + st, o = commands.getstatusoutput(cmd) + if st: return + dev_p = re.compile(r''' + ^(md\d+)\s*: # md device + \s*active\s* # "active" + (\w+)\s* # raid type + ''', re.VERBOSE) + + for s in o.splitlines(): + dev_res = dev_p.split(s) + if len(dev_res)>1: # matched + # dev_res[0] is '' + # dev_res[2] is 'raidX' + raid_dev = dev_res[1] + raid_level = dev_res[2][4:] + act_cmpts =[] + spr_cmpts =[] + for ss in dev_res[3].split(): + if re.search(r'\(S\)', ss): + spr_cmpts.append(ss[:ss.index('[')]) + else: + act_cmpts.append(ss[:ss.index('[')]) + + Raid(raid_dev, "yes", raid_level, act_cmpts, spr_cmpts) + + @staticmethod + def add_component(node, l): + ''' add component devices (in xml node) to list +node - xml node +l - list +''' + for sub in node.childNodes: + if sub.nodeType == sub.ELEMENT_NODE and sub.nodeName == 'component': + l.append(sub.firstChild.data.encode('ascii')) + + @staticmethod + def init_from_xml(node): + ''' create Raid instances from xml node ''' + for e in node.childNodes: + if e.nodeType == e.ELEMENT_NODE and e.nodeName == 'raid': + raid_dev = e.attributes['device'].value + raid_from = e.attributes['from_os'].value + raid_level = e.attributes['level'].value + act_cmpts = [] + spr_cmpts = [] + for sub_e in e.childNodes: + if sub_e.nodeType == sub_e.ELEMENT_NODE: + if sub_e.nodeName == 'active': + Raid.add_component(sub_e, act_cmpts) + elif sub_e.nodeName == 'spare': + Raid.add_component(sub_e, spr_cmpts) + + Raid(raid_dev, raid_from, raid_level, act_cmpts, spr_cmpts) + + @staticmethod + def to_xml(doc, p_node): + ''' write all raid instance into xml +doc - xml document instance +p_node - xml node (parent node) ''' + raids = doc.createElement('raids') + R_keys = Raid.dict.keys() + R_keys.sort() + for k in R_keys: + r = Raid.dict[k] + rd = doc.createElement('raid') + + rd_dev_attr = doc.createAttribute('device') + rd_dev_attr.value = r.device + rd.setAttributeNode(rd_dev_attr) + + rd_from_attr = doc.createAttribute('from_os') + rd_from_attr.value = r.from_os + rd.setAttributeNode(rd_from_attr) + + rd_level_attr = doc.createAttribute('level') + rd_level_attr.value = r.level + rd.setAttributeNode(rd_level_attr) + + rd_act_elem = doc.createElement('active') + for act_c in r.active_components: + act_c_e = doc.createElement('component') + act_c_tn = doc.createTextNode(act_c) + act_c_e.appendChild(act_c_tn) + rd_act_elem.appendChild(act_c_e) + rd.appendChild(rd_act_elem) + + rd_spr_elem = doc.createElement('spare') + for spr_c in r.spare_components: + spr_c_e = doc.createElement('component') + spr_c_tn = doc.createTextNode(spr_c) + spr_c_e.appendChild(spr_c_tn) + rd_spr_elem.appendChild(spr_c_e) + rd.appendChild(rd_spr_elem) + + raids.appendChild(rd) + + p_node.appendChild(raids) + #ri_tk_cmd.py use + @staticmethod + def get_size(dev): + ''' calculate raid device size ''' + rd = Raid.dict[dev] + sz = min([ float(Partition.dict[d].size) for d in rd.active_components]) + if rd.level == '0': return str(sz*len(rd.active_components)) + elif rd.level == '1': return str(sz*len(rd.active_components)/2) + elif rd.level == '5': return str(sz*(len(rd.active_components)-1)) + + @staticmethod + def get_next_device(): + ''' get next available raid device name ''' + num_p = re.compile(r'md(\d+)') + numbers = [ int(num_p.match(r).groups()[0]) for r in Raid.dict.keys()] + numbers.sort() + if numbers: + return 'md%d' %(int(numbers[-1])+1) + return 'md0' + + @staticmethod + def dev_in_raid(): + """if the device in raid or not""" + devices_in_raid=set() + for r in Raid.dict.values(): + devices_in_raid.update(r.active_components) + devices_in_raid.update(r.spare_components) + return devices_in_raid + +class MountPoint: + ''' mount-points ''' + dict={} + def __init__(self, dev, dir='', fs='', fm='no'): + self.device = dev + self.directory = dir + self.filesystem = fs + self.format = fm + MountPoint.dict[dev]=self + + @staticmethod + def change(dev, dir, fs, fm): + mp = MountPoint.dict[dev] + mp.directory = dir + mp.filesystem = fs + mp.format = fm + + @staticmethod + def init_from_internal(): + ''' init MountPoint from internal class Partition and class Raid ''' + # add raid device in dev_in_raid + dev_in_raid = Raid.dev_in_raid() + + for dev in set([ k for k in Partition.dict.keys() if not re.search('extended',Partition.dict[k].type)]).difference(set(dev_in_raid)): + if MountPoint.dict.has_key(dev): continue + MountPoint(dev, fs=Partition.dict[dev].filesystem) + + for dev in Raid.dict.keys(): + if MountPoint.dict.has_key(dev): continue + MountPoint(dev) + + # now delete unexist partition/raid mount point, or + # partition that used in raid. + s1 = set(MountPoint.dict.keys()) + s2 = set(Partition.dict.keys() + Raid.dict.keys()) - set(dev_in_raid) + for dev in s1-s2: + del MountPoint.dict[dev] + + + @staticmethod + def init_from_xml(node): + ''' create MountPoint instances from xml node ''' + for m in node.childNodes: + if m.nodeType == node.ELEMENT_NODE and m.nodeName == 'mount-point': + MountPoint(m.attributes['device'].value.encode('ascii'), \ + m.attributes['directory'].value.encode('ascii'), \ + m.attributes['file-system'].value.encode('ascii'), \ + m.attributes['format'].value.encode('ascii')) + + @staticmethod + def to_xml(doc, p_node): + ''' write all MountPoint instance into xml +doc - xml document instance +p_node - xml node (parent node)''' + mps = doc.createElement('mount-points') + M_keys = MountPoint.dict.keys() + M_keys.sort() + for k in M_keys: + m = MountPoint.dict[k] + mp = doc.createElement('mount-point') + dev_attr = doc.createAttribute('device') + dir_attr = doc.createAttribute('directory') + fs_attr = doc.createAttribute('file-system') + fm_attr = doc.createAttribute('format') + dev_attr.value = m.device + dir_attr.value = m.directory + fs_attr.value = m.filesystem + fm_attr.value = m.format + mp.setAttributeNode(dev_attr) + mp.setAttributeNode(dir_attr) + mp.setAttributeNode(fs_attr) + mp.setAttributeNode(fm_attr) + mps.appendChild(mp) + p_node.appendChild(mps) + + @staticmethod + def get_size(dev): + '''get size of this mount point ''' + if dev in Partition.dict.keys(): + return Partition.get_size(dev) + elif dev in Raid.dict.keys(): + return Raid.get_size(dev) + + @staticmethod + def check_data(dev,dr,fs): + '''check mount point is feasible. dr is mount point, dev is device''' + # check mount point is existent or not + if dr and dr in [ m.directory for m in MountPoint.dict.values() if m.device !=dev ]: + return False + + # check mount point is special or not + if dr in ['/etc','/bin','/sbin','/lib']: + return False + elif dr in ['/','/boot'] and fs in ['reiserfs','xfs']: + return False + + return True + +class Network: + ''' network ''' + hostname ='' + configuration ='' + domain = '' + ip = '' + mask = '' + gateway = '' + primary_dns = '' + secondary_dns = '' + + @staticmethod + def init_from_xml(node): + ''' init Network from xml node ''' + for k in node.attributes.keys(): + setattr(Network, k, node.attributes[k].value.encode('ascii')) + + @staticmethod + def to_xml(doc, p_node): + ''' write Network into xml +doc - xml document instance +p_node - xml node (parent node)''' + ntwk = doc.createElement('network') + for nm in ('hostname', 'configuration', 'ip', 'mask', 'gateway', 'primary_dns', 'secondary_dns', 'domain'): + to_xml_attr(doc, ntwk, Network, nm) + p_node.appendChild(ntwk) + +class Group: + ''' software package group ''' + dict = {} + def __init__(self, n, i): + self.name = n + self.install = i + self.description = '' + self.mandatory = [] + self.selection = 'manual' + self.optional = [] + Group.dict[n] = self + + @staticmethod + def handle_release_node(r_node): + ''' This method is called by init_from_config_xml +r_node - release node ''' + for i in r_node.childNodes: + if i.nodeType == i.ELEMENT_NODE and i.nodeName == 'including': + Group(i.attributes['group'].value, \ + i.attributes['install'].value == 'mandatory' and 'mandatory' or 'no') + + @staticmethod + def handle_group_node(g_node): + ''' This method is called by init_from_config_xml +g_node - group node ''' + g = Group.dict[g_node.attributes['name'].value] + for n in g_node.childNodes: + if n.nodeType == n.ELEMENT_NODE: + if n.nodeName == 'description': + g.description = n.firstChild.data + elif n.nodeName == 'including': + if n.attributes['install'].value == 'mandatory': + g.mandatory.append(n.attributes['package'].value.encode('ascii')) + else: + g.optional.append([n.attributes['package'].value.encode('ascii'), 'no']) + + @staticmethod + def init_from_config_xml(node): + ''' init Group instances from config xml + node - root node of config xml''' + rls_list = [r for r in node.childNodes + if r.nodeType == r.ELEMENT_NODE and r.nodeName == 'release'] + grp_list = [g for g in node.childNodes + if g.nodeType == g.ELEMENT_NODE and g.nodeName == 'group'] + Group.handle_release_node(rls_list[0]) + for g in grp_list: + Group.handle_group_node(g) + + @staticmethod + def init_from_install_xml(node): + ''' init Group instances from install xml + node - install xml 'groups' node''' + for grp in node.childNodes: + if grp.nodeType == grp.ELEMENT_NODE and grp.nodeName == 'group': + g = Group(grp.attributes['name'].value, grp.attributes['install'].value) + for grp_chld in grp.childNodes: + if grp_chld.nodeType == grp_chld.ELEMENT_NODE: + if grp_chld.nodeName == 'description': + g.description = grp_chld.firstChild.data + elif grp_chld.nodeName == 'mandatory': + g.mandatory.extend([pkg.attributes['name'].value for pkg in grp_chld.childNodes + if pkg.nodeType == pkg.ELEMENT_NODE and pkg.nodeName == 'package']) + elif grp_chld.nodeName == 'optional': + g.optional.extend([[pkg.attributes['name'].value, pkg.attributes['install'].value] for pkg in grp_chld.childNodes + if pkg.nodeType == pkg.ELEMENT_NODE and pkg.nodeName == 'package']) + g.selection = grp_chld.attributes['selection'].value + init_from_xml = init_from_install_xml + @staticmethod + def to_xml(doc, p_node): + ''' write Group instances into xml +doc - xml document instance +p_node - xml node (parent node)''' + grps = doc.createElement('groups') + for g in Group.dict.values(): + grp = doc.createElement('group') + name_attr = doc.createAttribute('name') + name_attr.value = g.name + grp.setAttributeNode(name_attr) + + inst_attr = doc.createAttribute('install') + inst_attr.value = g.install + grp.setAttributeNode(inst_attr) + + if g.description: + dscp = doc.createElement('description') + dscp_data = doc.createTextNode(g.description) + dscp.appendChild(dscp_data) + grp.appendChild(dscp) + + if g.mandatory: + mndt = doc.createElement('mandatory') + for m in g.mandatory: + pkg = doc.createElement('package') + pname_attr = doc.createAttribute('name') + pname_attr.value = m + pkg.setAttributeNode(pname_attr) + + mndt.appendChild(pkg) + grp.appendChild(mndt) + + if g.optional: + optl = doc.createElement('optional') + sel_attr = doc.createAttribute('selection') + sel_attr.value = g.selection + optl.setAttributeNode(sel_attr) + for o in g.optional: + pkg = doc.createElement('package') + pname_attr = doc.createAttribute('name') + pname_attr.value = o[0] + pkg.setAttributeNode(pname_attr) + pinst_attr = doc.createAttribute('install') + pinst_attr.value = o[1] + pkg.setAttributeNode(pinst_attr) + optl.appendChild(pkg) + grp.appendChild(optl) + grps.appendChild(grp) + p_node.appendChild(grps) + @staticmethod + def get_install_pkgs(): + ''' get package names that should be installed ''' + l = [] + for g in Group.dict.values(): + if g.install != 'no': + l.extend([ p for p in g.mandatory ]) + if g.selection == 'manual': + l.extend([ p[0] for p in g.optional if p[1] == 'yes' ]) + elif g.selection == 'all': + l.extend([ p[0] for p in g.optional ]) + else: # selection is 'none' + pass + return l + +class Service: + ''' service ''' + list = [] + def __init__(self, nm, nb, sc, st, p): + ''' init method +nm - name +nb - number +sc - script +st - start (yes/no/disable) +p - pkg, that includes this service +''' + self.name = nm + self.number = nb + self.script = sc + self.start = st + self.package = p + Service.list.append(self) + + @staticmethod + def init_from_config_xml(root): + ''' init Service instances from config xml + root - root node of config xml ''' + for p in root.childNodes: + if p.nodeType == p.ELEMENT_NODE and p.nodeName == 'package': + for s in p.childNodes: + if s.nodeType == s.ELEMENT_NODE and s.nodeName == 'service': + Service(s.attributes['name'].value, \ + s.attributes['number'].value, \ + s.attributes['script'].value, \ + 'disable', \ + p.attributes['name'].value ) + + @staticmethod + def change_state(): + ''' Based on package information in Group class, change 'start' data member + from 'disable' to 'no', or from 'yes'/'no' to 'disable' ''' + l = Group.get_install_pkgs() + + for s in Service.list: + if s.package in l: + if s.start == 'disable': + s.start = 'no' + else: + s.start = 'disable' + + @staticmethod + def init_from_install_xml(node): + ''' init Service instances from install xml +node - install xml 'services' node''' + for srv in node.childNodes: + if srv.nodeType == srv.ELEMENT_NODE and srv.nodeName == 'service': + Service(srv.attributes['name'].value, \ + srv.attributes['number'].value, \ + srv.attributes['script'].value, \ + srv.attributes['start'].value, \ + srv.attributes['package'].value) + + init_from_xml = init_from_install_xml + @staticmethod + def to_xml(doc, p_node): + ''' write Service instances into xml +doc - xml document instance +p_node - xml node (parent node)''' + srvs = doc.createElement('services') + for s in Service.list: + srv = doc.createElement('service') + for n in ('name', 'number', 'script', 'start', 'package'): + to_xml_attr(doc, srv, s, n) + srvs.appendChild(srv) + p_node.appendChild(srvs) + +def prepar_installation(): + '''create file called 'intall.xml',which contains the information about the installation''' + to_xml() + xmldoc = minidom.parse(install_xml) + root = xmldoc.firstChild + return root.toxml() + +def init(): + ''' initialize ''' + Partition.init_from_os() + Raid.init_from_os() + MountPoint.init_from_internal() + xmldoc_cfg = minidom.parse(config_xml) + root_cfg = xmldoc_cfg.firstChild + Group.init_from_config_xml(root_cfg) + Service.init_from_config_xml(root_cfg) + +def init_from_xml(): + ''' get data in file (install.xml)''' + xmldoc = minidom.parse(install_xml) + root = xmldoc.firstChild + for n in root.childNodes: + if n.nodeType == n.ELEMENT_NODE: + if n.nodeName == 'serial-number': + SerialNumber.init_from_xml(n) + elif n.nodeName == 'partitions': + Partition.init_from_xml(n) + elif n.nodeName == 'raids': + Raid.init_from_xml(n) + elif n.nodeName == 'mount-points': + MountPoint.init_from_xml(n) + elif n.nodeName == 'network': + Network.init_from_xml(n) + elif n.nodeName == 'groups': + Group.init_from_xml(n) + elif n.nodeName == 'services': + Service.init_from_xml(n) + +def to_xml(): + ''' write internal data into xml file ''' + f = file(install_xml, 'w') + xmldoc = minidom.Document() + root = xmldoc.createElement('install') + xmldoc.appendChild(root) + + SerialNumber.to_xml(xmldoc, root) + #Partition.to_xml(xmldoc, root) + #Raid.to_xml(xmldoc, root) + #MountPoint.to_xml(xmldoc, root) + #Network.to_xml(xmldoc, root) + #Group.to_xml(xmldoc, root) + #Service.to_xml(xmldoc, root) + + PrettyPrint(xmldoc, f) + f.close() diff --git a/dialog/di_dialog.py b/dialog/di_dialog.py new file mode 100644 index 0000000..4472afd --- /dev/null +++ b/dialog/di_dialog.py @@ -0,0 +1,19 @@ +import dialog + +d=dialog.Dialog() +d.setBackgroundTitle("Rocky 4.2 install") + +class SerialNumber: + def __init__(self,height=10, width=30,text='plase input a serial number'): + self.text=text + self.height=height + self.width=width + + def show(self): + d.inputbox(self.text,self.height,self.width) + + def to_xml(): + + +S=SerialNumber(10,50) +S.show()