X-Git-Url: https://git.whamcloud.com/?p=fs%2Flustre-release.git;a=blobdiff_plain;f=lustre%2Futils%2Flmc;h=335572af7cda90d9a1222ebd773160761360586b;hp=2ae47a24f828ac224ca6b2318de2f57538410954;hb=f01c9cd26f972e98460d7f3609ca575a30b4226e;hpb=620026ea62f08f2c10ed226974941e5e1175d71c diff --git a/lustre/utils/lmc b/lustre/utils/lmc index 2ae47a2..335572a 100755 --- a/lustre/utils/lmc +++ b/lustre/utils/lmc @@ -19,100 +19,267 @@ # """ -lmc - lustre configurtion data manager - - Basic plan for lmc usage: -# create nodes -./lmc --output config.xml --node server --net server1 tcp -./lmc --merge config.xml --node client --net client1 tcp -./lmc --merge config.xml --node client --route gw lo [hi] -./lmc --merge config.xml --router --node gw1 --net gw1 tcp -./lmc --merge config.xml --node gw1 --net 1 elan +lmc - lustre configuration data manager + + See Lustre book (http://www.lustre.org/docs/lustre.pdf) for documentation on lmc. -./lmc --merge config.xml --route elan 1 1 100 -./lmc --merge config.xml --route tcp gw1 ba1 +""" +import sys, os, getopt, string, exceptions, re +import xml.dom.minidom +def printDoc(doc, stream=sys.stdout): + try: + from xml.dom.ext import PrettyPrint + PrettyPrint(doc, stream) + except ImportError: + stream.write(doc.toxml()) + stream.write("\n") + + +PYMOD_DIR = "/usr/lib/lustre/python" + +def development_mode(): + base = os.path.dirname(sys.argv[0]) + if os.access(base+"/Makefile.am", os.R_OK): + return 1 + return 0 + +if not development_mode(): + sys.path.append(PYMOD_DIR) + +import Lustre + +DEFAULT_PORT = 988 +DEFAULT_STRIPE_SZ = 1048576 +DEFAULT_STRIPE_CNT = 1 +DEFAULT_STRIPE_PATTERN = 0 +UUID_MAX_LENGTH = 31 + +def reference(): + print """usage: lmc --add object [object parameters] + +Object creation command summary: + +--add node + --node node_name + --timeout num + --upcall path + --lustre_upcall path + --portals_upcall path + --ptldebug debug_level + --subsystem subsystem_name + +--add net + --node node_name + --nid nid + --cluster_id + --nettype tcp|elan|gm + --hostaddr addr + --port port + --tcpbuf size + --irq_affinity 0|1 + --router + +--add mds + --node node_name + --mds mds_name + --failover + --dev path + --backdev path + --fstype ldiskfs|ext3 + --backfstype ldiskfs|ext3|tmpfs + --size size + --nspath + --journal_size size + --inode_size size + --lmv lmv_name + --mkfsoptions options + --mountfsoptions options + +--add lov + --lov lov_name + --mds mds_name + --stripe_sz num + --stripe_cnt num + --stripe_pattern num + --lmv lmv_name + +--add ost + --node node_name + --ost ost_name + --failover + --lov lov_name + --index index + --dev path + --backdev path + --size size + --fstype ldiskfs|ext3 + --backfstype ldiskfs|ext3|tmpfs + --journal_size size + --inode_size size + --osdtype obdecho|obdfilter + --ostuuid uuid + --mkfsoptions options + --mountfsoptions options + --nspath + +--delete ost + --node node_name + --ost ost_name + --migrate +--deactivate ost + --node node_name + --ost ost_name + +--add mtpt - Mountpoint + --node node_name + --path /mnt/point + --mds mds_name + --lmv lmv_name + --ost ost_name OR --lov lov_name + --clientoptions options + +--add route + --node nodename + --router + --gw nid + --gateway_cluster_id nid + --target_cluster_id nid + --lo nid + --hi nid + +--add echo_client + --node nodename + +--add mgmt - Management/monitoring service + --node node_name + --mgmt mgmt_service_name + +--add lmv + --lmv lmv_name + +--add cobd + --node node_name + --real_obd obd_name + --cache_obd obd_name + +--add cmobd + --node node_name + --master_dev obd_name + --cache_dev obd_name + +--commit - Close a configuration version, and start a new one +""" -# configure server -./lmc --merge config.xml --node server --mds mds1 /tmp/mds1 50000 +PARAM = Lustre.Options.PARAM +lmc_options = [ + # lmc input/output options + ('reference', "Print short reference for commands."), + ('verbose,v', "Print system commands as they are run."), + ('merge,m', "Append to the specified config file.", PARAM), + ('output,o', "Write XML configuration into given output file. Overwrite existing content.", PARAM), + ('input,i', "", PARAM), + ('batch', "Used to execute lmc commands in batch mode.", PARAM), + + # commands + ('add', "", PARAM), + ('delete', "", PARAM), + ('deactivate', "", PARAM), + ('commit', "Commit all config changes and start a new version"), + + # node options + ('node', "Add a new node in the cluster configuration.", PARAM), + ('timeout', "Set timeout to initiate recovery.", PARAM), + ('upcall', "Set both lustre and portals upcall scripts.", PARAM), + ('lustre_upcall', "Set location of lustre upcall script.", PARAM), + ('portals_upcall', "Set location of portals upcall script.", PARAM), + ('ptldebug', "Set the portals debug level", PARAM), + ('subsystem', "Specify which Lustre subsystems have debug output recorded in the log", PARAM), + + # network + ('nettype', "Specify the network type. This can be tcp/elan/gm.", PARAM), + ('nid', "Give the network ID, e.g ElanID/IP Address as used by portals.", PARAM), + ('tcpbuf', "Optional argument to specify the TCP buffer size.", PARAM, "0"), + ('port', "Optional argument to specify the TCP port number.", PARAM, DEFAULT_PORT), + ('irq_affinity', "Optional argument.", PARAM, 0), + ('hostaddr', "", PARAM,""), + ('cluster_id', "Specify the cluster ID", PARAM, "0"), + + # routes + ('route', "Add a new route for the cluster.", PARAM), + ('router', "Optional flag to mark a node as router."), + ('gw', "Specify the nid of the gateway for a route.", PARAM), + ('gateway_cluster_id', "", PARAM, "0"), + ('target_cluster_id', "", PARAM, "0"), + ('lo', "For a range route, this is the low value nid.", PARAM), + ('hi', "For a range route, this is a hi value nid.", PARAM,""), + + # servers: mds and ost + ('mds', "Specify MDS name.", PARAM,""), + ('ost', "Specify the OST name.", PARAM,""), + ('osdtype', "This could obdfilter or obdecho.", PARAM, "obdfilter"), + ('failover', "Enable failover support on OSTs or MDS?"), + ('group', "", PARAM), + ('dev', "Path of the device on local system.", PARAM,""), + ('backdev', "Path of the device for backing storage on local system.", PARAM,""), + ('size', "Specify the size of the device if needed.", PARAM,"0"), + ('journal_size', "Specify new journal size for underlying file system.", PARAM,"0"), + ('inode_size', "Specify new inode size for underlying file system.", PARAM,"0"), + ('fstype', "Optional argument to specify the filesystem type.", PARAM, "ext3"), + ('backfstype', "Optional argument to specify the backing filesystem type.", PARAM, "ext3"), + ('mkfsoptions', "Optional argument to mkfs.", PARAM, ""), + ('mountfsoptions', "Optional argument to mount fs.", PARAM, ""), + ('ostuuid', "", PARAM,""), + ('nspath', "Local mount point of server namespace.", PARAM,""), + ('format', ""), + ('migrate', "used for offline migrate of an ost in conjunctio with add/delete"), + + # clients: mountpoint and echo + ('echo_client', "", PARAM), + ('path', "Specify the mountpoint for Lustre.", PARAM), + ('filesystem', "Lustre filesystem name", PARAM,""), + ('clientoptions', "Specify the options for Lustre, such as async.", PARAM, ""), + + # lov + ('lov', "Specify LOV name.", PARAM,""), + ('index', "Specify index for OBD in LOV target table.", PARAM), + ('stripe_sz', "Specify the stripe size in bytes.", PARAM), + ('stripe_cnt', "Specify the number of OSTs each file should be striped on.", PARAM, 0), + ('stripe_pattern', "Specify the stripe pattern. RAID 0 is the only one currently supported.", PARAM, 0), + + # cobd + + ('real_obd', "Specify the real device for the cache obd system.", PARAM), + ('cache_obd', "Specify the cache device for the cache obd system.", PARAM), + ('cobd', "Specify COBD name", PARAM), -# create lov -./lmc --merge config.xml --lov lov1 mds1 65536 0 0 -./lmc --merge config.xml --node server --lov lov1 --ost /tmp/ost1 100000 -./lmc --merge config.xml --node server --lov lov1 --ost /tmp/ost2 100000 + # cmobd + ('master_dev', "Specify the master device for the cmobd system.", PARAM), + ('cache_dev', "Specify the cache device for the cmobd obd system.", PARAM), + ('cmobd', "Specify COBD name", PARAM), -# create client config -./lmc --merge config.xml --node client --mtpt /mnt/lustre mds1 lov1 -""" + ('mgmt', "Specify management/monitoring service name.", PARAM, ""), -import sys, os, getopt, string -import xml.dom.minidom -from xml.dom.ext import PrettyPrint - - -DEFAULT_PORT = 988 # XXX What is the right default acceptor port to use? - -def usage(): - print """usage: lmc [--node --ost | --mtpt | --lov] args -Commands: ---node node_name - Node_name by itself it will create a new node. If the --router - option is used when creating a new node, then that node will also - be configured as a router. When used with other commands it - specifies the node to modify. - ---net hostname nettype [port, recv_buf, send_buf] - Nettype is either tcp, elan, or gm. - Requires --node - ---route net gw lo [hi] - This command is used to create routes. NET is the - network type this route will be used on. The GW is an address of - one of the local interfaces. LO and HI represent a range of - addresses that can be reached through the gateway. If HI is not - set, then a route to the specific host in LO is created. - ---mds device [size] - Create a MDS using the device - Requires --node - ---lov lov_name [mdc_name stripe_sz stripe_off pattern] - Creates a logical volume - When used with other commands, it specifics the lov to modify - ---mdc mdc_name - Configures a MDC for a node. - Requires --node - ---ost device [size] - Creates an OBD/OST/OSC configuration triplet for a new device. - When used on "host", the device will be initialized and the OST - will be enabled. On client nodes, the OSC will be avaiable. - Requires --node - If --lov lov_name is used, this device is added to lov. - ---mtpt /mnt/point mds_name lov_name|osc_name - Creates a client mount point. - Requires --node - -Options: ---merge="xml file" Add the new objects to an existing file ---format Format the partitions if unformated ---reformat Reformat partitions (this should be an lconf arg, - I think) ---obdtype="obdtype" Specifiy obdtype: valid ones are obdecho and obdfilter. - This is only useful for the --ost command. - The device parameters are ignored for the obdecho type. -""" - sys.exit(1) + # lmv + ('lmv', "Specify LMV name.", PARAM,""), + ] def error(*args): msg = string.join(map(str,args)) + raise OptionError("Error: " + msg) + +def panic(cmd, msg): + print "! " + cmd print msg sys.exit(1) - + + +def warning(*args): + msg = string.join(map(str,args)) + print "Warning: ", msg + # # manage names and uuids # need to initialize this by walking tree to ensure @@ -129,17 +296,28 @@ def new_name(base): return ret def new_uuid(name): - return "%s_UUID" % (name) + ctr = 2 + ret = "%s_UUID" % (name) + if len(ret) > UUID_MAX_LENGTH: + ret = ret[-UUID_MAX_LENGTH:] + while uuids.has_key(ret): + ret = "%s_UUID_%d" % (name, ctr) + ctr = 1 + ctr + if len(ret) > UUID_MAX_LENGTH: + ret = ret[-UUID_MAX_LENGTH:] + uuids[ret] = 1 + return ret + ldlm_name = 'ldlm' ldlm_uuid = 'ldlm_UUID' + def new_lustre(dom): """Create a new empty lustre document""" # adding ldlm here is a bit of a hack, but one is enough. - str = """ + str = """ - - """ % (ldlm_name, ldlm_uuid) + """ % (Lustre.CONFIG_VERSION, ldlm_name, ldlm_uuid) return dom.parseString(str) names = {} @@ -157,9 +335,8 @@ def init_names(doc): init_names(n) def get_format_flag(options): - if options.has_key('format'): - if options['format']: - return 'yes' + if options.format: + return 'yes' return 'no' ############################################################ @@ -177,14 +354,20 @@ class GenConfig: ref = self.doc.createElement(tag) ref.setAttribute("uuidref", uuid) return ref - + + def dev(self, devname): + """ generate """ + tgt = self.doc.createElement('dev') + tgt.setAttribute("dev", devname) + return tgt + def newService(self, tag, name, uuid): """ create a new service elmement, which requires name and uuid attributes """ new = self.doc.createElement(tag) - new.setAttribute("name", name); new.setAttribute("uuid", uuid); + new.setAttribute("name", name); return new - + def addText(self, node, str): txt = self.doc.createTextNode(str) node.appendChild(txt) @@ -198,31 +381,51 @@ class GenConfig: node.appendChild(new) return new - def network(self, name, uuid, hostname, net, port=0, tcpbuf=0): + def network(self, name, uuid, nid, cluster_id, net, hostaddr="", + port=0, tcpbuf=0, irq_aff=0): """create node""" network = self.newService("network", name, uuid) - network.setAttribute("type", net); - self.addElement(network, "server", hostname) + network.setAttribute("nettype", net); + self.addElement(network, "nid", nid) + self.addElement(network, "clusterid", cluster_id) + if hostaddr: + self.addElement(network, "hostaddr", hostaddr) if port: self.addElement(network, "port", "%d" %(port)) if tcpbuf: - self.addElement(network, "send_mem", "%d" %(tcpbuf)) - self.addElement(network, "recv_mem", "%d" %(tcpbuf)) - + self.addElement(network, "sendmem", "%d" %(tcpbuf)) + self.addElement(network, "recvmem", "%d" %(tcpbuf)) + if irq_aff: + self.addElement(network, "irqaffinity", "%d" %(irq_aff)) + return network - def route(self, lo, hi): + def routetbl(self, name, uuid): + """create node""" + rtbl = self.newService("routetbl", name, uuid) + return rtbl + + def route(self, gw_net_type, gw, gw_cluster_id, tgt_cluster_id, lo, hi): """ create one entry for the route table """ ref = self.doc.createElement('route') + ref.setAttribute("type", gw_net_type) + ref.setAttribute("gw", gw) + ref.setAttribute("gwclusterid", gw_cluster_id) + ref.setAttribute("tgtclusterid", tgt_cluster_id) ref.setAttribute("lo", lo) if hi: ref.setAttribute("hi", hi) return ref - - def node(self, name, uuid): + + def profile(self, name, uuid): + """ create a host """ + profile = self.newService("profile", name, uuid) + return profile + + def node(self, name, uuid, prof_uuid): """ create a host """ node = self.newService("node", name, uuid) - self.addElement(node, 'profile') + node.appendChild(self.ref("profile", prof_uuid)) return node def ldlm(self, name, uuid): @@ -230,65 +433,180 @@ class GenConfig: ldlm = self.newService("ldlm", name, uuid) return ldlm - def obd(self, name, uuid, fs, obdtype, devname, format, dev_size=0): - obd = self.newService("obd", name, uuid) - obd.setAttribute('type', obdtype) - if fs: - self.addElement(obd, "fstype", fs) + def osd(self, name, uuid, fstype, osdtype, devname, format, ost_uuid, + node_uuid, dev_size=0, journal_size=0, inode_size=0, nspath="", + mkfsoptions="", mountfsoptions="", backfstype="", backdevname=""): + osd = self.newService("osd", name, uuid) + osd.setAttribute('osdtype', osdtype) + osd.appendChild(self.ref("target", ost_uuid)) + osd.appendChild(self.ref("node", node_uuid)) + osd.appendChild(self.dev(devname)) + + if fstype: + self.addElement(osd, "fstype", fstype) + if backfstype: + self.addElement(osd, "backfstype", backfstype) + if backdevname: + self.addElement(osd, "backdevpath", backdevname) if devname: - dev = self.addElement(obd, "device", devname) - if (dev_size): - dev.setAttribute("size", "%s" % (dev_size)) - self.addElement(obd, "autoformat", format) - return obd - - def osc(self, name, uuid, obd_uuid, net_uuid): - osc = self.newService("osc", name, uuid) - osc.appendChild(self.ref("ost", net_uuid)) - osc.appendChild(self.ref("obd", obd_uuid)) - return osc - - def ost(self, name, uuid, obd_uuid, net_uuid): + dev = self.addElement(osd, "devpath", devname) + self.addElement(osd, "autoformat", format) + if dev_size: + self.addElement(osd, "devsize", "%s" % (dev_size)) + if journal_size: + self.addElement(osd, "journalsize", "%s" % (journal_size)) + if inode_size: + self.addElement(osd, "inodesize", "%s" % (inode_size)) + if mkfsoptions: + self.addElement(osd, "mkfsoptions", mkfsoptions) + if mountfsoptions: + self.addElement(osd, "mountfsoptions", mountfsoptions) + if nspath: + self.addElement(osd, "nspath", nspath) + return osd + + def cobd(self, name, uuid, real_uuid, cache_uuid): + cobd = self.newService("cobd", name, uuid) + cobd.appendChild(self.ref("realobd",real_uuid)) + cobd.appendChild(self.ref("cacheobd",cache_uuid)) + return cobd + + def cmobd(self, name, uuid, real_uuid, cache_uuid): + cmobd = self.newService("cmobd", name, uuid) + cmobd.appendChild(self.ref("masterobd",real_uuid)) + cmobd.appendChild(self.ref("cacheobd",cache_uuid)) + return cmobd + + def ost(self, name, uuid, osd_uuid, group=""): ost = self.newService("ost", name, uuid) - ost.appendChild(self.ref("network", net_uuid)) - ost.appendChild(self.ref("obd", obd_uuid)) + ost.appendChild(self.ref("active", osd_uuid)) + if group: + self.addElement(ost, "group", group) return ost - def lov(self, name, uuid, mds_uuid, stripe_sz, stripe_off, pattern): + def oss(self, name, uuid): + oss = self.newService("oss", name, uuid) + return oss + + def lov(self, name, uuid, mds_uuid, stripe_sz, stripe_cnt, pattern): lov = self.newService("lov", name, uuid) lov.appendChild(self.ref("mds", mds_uuid)) - devs = self.addElement(lov, "devices" ) - devs.setAttribute("stripesize", stripe_sz) - devs.setAttribute("stripeoffset", stripe_off) - devs.setAttribute("pattern", pattern) + lov.setAttribute("stripesize", str(stripe_sz)) + lov.setAttribute("stripecount", str(stripe_cnt)) + lov.setAttribute("stripepattern", str(pattern)) return lov - def mds(self, name, uuid, fs, devname, format, net_uuid, node_uuid, - failover_uuid = "", dev_size=0 ): + def lov_tgt(self, obd_uuid, index, generation): + tgt = self.doc.createElement('lov_tgt') + tgt.setAttribute("uuidref", obd_uuid) + tgt.setAttribute("index", index) + tgt.setAttribute("generation", generation) + tgt.setAttribute("active", '1') + return tgt + + def lovconfig(self, name, uuid, lov_uuid): + lovconfig = self.newService("lovconfig", name, uuid) + lovconfig.appendChild(self.ref("lov", lov_uuid)) + return lovconfig + + def lmv(self, name, uuid): + lmv = self.newService("lmv", name, uuid) + return lmv + + def mds(self, name, uuid, mdd_uuid, group="", lmv=""): mds = self.newService("mds", name, uuid) - self.addElement(mds, "fstype", fs) - dev = self.addElement(mds, "device", devname) - if dev_size: - dev.setAttribute("size", "%s" % (dev_size)) - self.addElement(mds, "autoformat", format) - mds.appendChild(self.ref("network", net_uuid)) - mds.appendChild(self.ref("node", node_uuid)) - if failover_uuid: - mds.appendChild(self.ref("failover", failover_uuid)) + mds.appendChild(self.ref("active",mdd_uuid)) + if group: + self.addElement(mds, "group", group) return mds - def mdc(self, name, uuid, mds_uuid): - mdc = self.newService("mdc", name, uuid) - mdc.appendChild(self.ref("mds", mds_uuid)) - return mdc - - def mountpoint(self, name, uuid, mdc_uuid, osc_uuid, path): + def mdsdev(self, name, uuid, fstype, devname, format, node_uuid, + mds_uuid, dev_size=0, journal_size=0, inode_size=256, + nspath="", mkfsoptions="", mountfsoptions="", backfstype="", + backdevname="", lmv_uuid=""): + mdd = self.newService("mdsdev", name, uuid) + self.addElement(mdd, "fstype", fstype) + if backfstype: + self.addElement(mdd, "backfstype", backfstype) + dev = self.addElement(mdd, "devpath", devname) + if backdevname: + self.addElement(mdd, "backdevpath", backdevname) + self.addElement(mdd, "autoformat", format) + if dev_size: + self.addElement(mdd, "devsize", "%s" % (dev_size)) + if journal_size: + self.addElement(mdd, "journalsize", "%s" % (journal_size)) + if inode_size: + self.addElement(mdd, "inodesize", "%s" % (inode_size)) + if nspath: + self.addElement(mdd, "nspath", nspath) + if mkfsoptions: + self.addElement(mdd, "mkfsoptions", mkfsoptions) + if mountfsoptions: + self.addElement(mdd, "mountfsoptions", mountfsoptions) + + mdd.appendChild(self.ref("node", node_uuid)) + mdd.appendChild(self.ref("target", mds_uuid)) + mdd.appendChild(self.dev(devname)) + + if lmv_uuid: + mdd.appendChild(self.ref("lmv", lmv_uuid)) + self.addElement(mdd, "lmv", lmv_uuid) + + return mdd + + def mgmt(self, mgmt_name, mgmt_uuid, node_uuid): + mgmt = self.newService("mgmt", mgmt_name, mgmt_uuid) + mgmt.appendChild(self.ref("node", node_uuid)) + # Placeholder until mgmt-service failover. + mgmt.appendChild(self.ref("active", mgmt_uuid)) + return mgmt + + def mountpoint(self, name, uuid, fs_uuid, path, clientoptions): mtpt = self.newService("mountpoint", name, uuid) - mtpt.appendChild(self.ref("mdc", mdc_uuid)) - mtpt.appendChild(self.ref("osc", osc_uuid)) + mtpt.appendChild(self.ref("filesystem", fs_uuid)) self.addElement(mtpt, "path", path) + if clientoptions: + self.addElement(mtpt, "clientoptions", clientoptions) return mtpt + def filesystem(self, name, uuid, mds_uuid, obd_uuid, mgmt_uuid): + fs = self.newService("filesystem", name, uuid) + fs.appendChild(self.ref("mds", mds_uuid)) + fs.appendChild(self.ref("obd", obd_uuid)) + if mgmt_uuid: + fs.appendChild(self.ref("mgmt", mgmt_uuid)) + return fs + + def echo_client(self, name, uuid, osc_uuid): + ec = self.newService("echoclient", name, uuid) + ec.appendChild(self.ref("obd", osc_uuid)) + return ec + + def update(self, version): + new = self.doc.createElement("update") + new.setAttribute("version", version) + return new + + def add(self, lov, ost, index, gen): + new = self.doc.createElement("add") + new.setAttribute("lov_uuidref", lov) + new.setAttribute("ost_uuidref", ost) + new.setAttribute("index", index) + new.setAttribute("generation", gen) + return new + + def delete(self, lov, ost, index, gen, options): + if options.delete: + new = self.doc.createElement("delete") + else: + new = self.doc.createElement("deactivate") + new.setAttribute("lov_uuidref", lov) + new.setAttribute("ost_uuidref", ost) + new.setAttribute("index", index) + new.setAttribute("generation", gen) + return new + ############################################################ # Utilities to query a DOM tree # Using this functions we can treat use config information @@ -299,6 +617,40 @@ def getName(n): def getUUID(node): return node.getAttribute('uuid') +def findLastUpdate(lustre): + node = None + version = 0 + for n in lustre.childNodes: + if n.nodeType == n.ELEMENT_NODE: + if n.nodeName != 'update': + continue + tmp = int(n.getAttribute('version')) + if not tmp: + error('malformed XML: update tag without a version attribute') + if tmp != version + 1: + error('malformed XML: expecting update record '+str(version + 1)+', found '+str(tmp)+'.') + version = tmp + node = n + return node + +def addUpdate(gen, lustre, node): + update = findLastUpdate(lustre) + if not update: + return + #add_record = update.getElementsByTagName('add') + #if not add_record: + # add_record = gen.add() + # update.appendChild(add_record) + #else: + # add_record = add_record[0] + #add_record.appendChild(node) + update.appendChild(node) + +def delUpdate(gen, lustre, node): + update = findLastUpdate(lustre) + if not update: + return + update.appendChild(node) def findByName(lustre, name, tag = ""): for n in lustre.childNodes: @@ -322,30 +674,23 @@ def lookup(node, uuid): n = lookup(n, uuid) if n: return n return None - - -def mds2node(lustre, mds_name): - """ Find the node a MDS is configured on """ - mds = findByName(lustre, mds_name, 'mds') - ref = mds.getElementsByTagName('node_ref') - if not ref: - error("no node found for:", mds_name) - node_uuid = ref[0].getAttribute('uuidref') - node = lookup(lustre, node_uuid) - if not node: - error("no node found for :", mds_name) - return node def name2uuid(lustre, name, tag="", fatal=1): ret = findByName(lustre, name, tag) if not ret: if fatal: - error('name2uuid:', name, "not found.") + error('name2uuid:', '"'+name+'"', tag, 'element not found.') else: return "" return getUUID(ret) - + +def lookup_filesystem(lustre, mds_uuid, ost_uuid): + for n in lustre.childNodes: + if n.nodeType == n.ELEMENT_NODE and n.nodeName == 'filesystem': + if ref_exists(n, mds_uuid) and ref_exists(n, ost_uuid): + return getUUID(n) + return None # XXX: assumes only one network element per node. will fix this # as soon as support for routers is added @@ -353,27 +698,157 @@ def get_net_uuid(lustre, node_name): """ get a network uuid for a node_name """ node = findByName(lustre, node_name, "node") if not node: - error ("node not found:", node_name) + error ('get_net_uuid:', '"'+node_name+'"', "node element not found.") net = node.getElementsByTagName('network') if net: return getUUID(net[0]) return None - -def lov_add_osc(gen, lov, osc_uuid): - devs = lov.getElementsByTagName('devices') - if len(devs) == 1: - devs[0].appendChild(gen.ref("osc", osc_uuid)) +def lov_mod_obd(gen, lustre, lov, tgt, osc_uuid, options): + tgt.setAttribute('uuidref', osc_uuid) + if options.migrate: + gener = int(tgt.getAttribute('generation')) else: - error("No devices element found for LOV:", lov) - + gener = int(tgt.getAttribute('generation')) + 1 + tgt.setAttribute('generation', str(gener)) + tgt.setAttribute('active', '1') + lov_index = int(tgt.getAttribute('index')) + addUpdate(gen, lustre, gen.add(getUUID(lov), osc_uuid, str(lov_index), + str(gener))) + return + +def lov_add_obd(gen, lustre, lov, osc_uuid, options): + lov_name = getName(lov) + if options.index: + lov_index = get_option_int(options, 'index') + for tgt in lustre.getElementsByTagName('lov_tgt'): + if str(lov_index) == tgt.getAttribute('index'): + uuidref = tgt.getAttribute('uuidref') + if uuidref != '': + raise OptionError("%s --index %d is still in use: %s" % + (lov_name, lov_index, uuidref)) + lov_mod_obd(gen, lustre, lov, tgt, osc_uuid, options) + return + else: + lov_index = 0 + for tgt in lustre.getElementsByTagName('lov_tgt'): + uuidref = tgt.getAttribute('uuidref') + tmp = int(tgt.getAttribute('index')) + if tmp != lov_index: + error('malformed xml: LOV targets are not ordered; found index '+str(tmp)+', expected '+str(lov_index)+'.') + uuidref = tgt.getAttribute('uuidref') + if uuidref == '': + lov_mod_obd(gen, lustre, lov, tgt, osc_uuid, options) + return + lov_index = lov_index + 1 + + lov.appendChild(gen.lov_tgt(osc_uuid, str(lov_index), '1')) + addUpdate(gen, lustre, gen.add(getUUID(lov), osc_uuid, str(lov_index), '1')) + +def lov_del_obd(gen, lustre, lov, osc_uuid, options): + lov_name = getName(lov) + if options.index: + lov_index = get_option_int(options, 'index') + for tgt in lustre.getElementsByTagName('lov_tgt'): + index = tgt.getAttribute('index') + if index == lov_index: + uuidref = tgt.getAttribute('uuidref') + if uuidref != osc_uuid: + raise OptionError("%s --index %d contains %s, not %s" % + (lov_name, lov_index, osc_uuid, uuidref)) + if options.delete: + tgt.setAttribute('uuidref', '') + + # bump the generation just in case... + if options.migrate: + gen = int(tgt.getAttribute('generation')) + else: + gen = int(tgt.getAttribute('generation')) + 1 + + tgt.setAttribute('active', '0') + tgt.setAttribute('generation', str(gen)) + return + raise OptionError("%s --index %d not in use by %s." % + (lov_name, lov_index, osc_uuid)) + + for tgt in lustre.getElementsByTagName('lov_tgt'): + uuidref = tgt.getAttribute('uuidref') + if uuidref == osc_uuid: + genera = int(tgt.getAttribute('generation')) + delete_rec = gen.delete(getUUID(lov), + osc_uuid,tgt.getAttribute('index'), + str(genera), options) + delUpdate(gen, lustre, delete_rec) + + if options.delete: + tgt.setAttribute('uuidref', '') + if not options.migrate: + genera = genera + 1 + tgt.setAttribute('active', '0') + tgt.setAttribute('generation', str(genera)) + +def lmv_add_obd(gen, lmv, mdc_uuid): + lmv.appendChild(gen.ref("mds", mdc_uuid)) +def ref_exists(profile, uuid): + elist = profile.childNodes + for e in elist: + if e.nodeType == e.ELEMENT_NODE: + ref = e.getAttribute('uuidref') + if ref == uuid: + return 1 + return 0 + +# ensure that uuid is not already in the profile +# return true if uuid is added def node_add_profile(gen, node, ref, uuid): - ret = node.getElementsByTagName('profile') + refname = "%s_ref" % "profile" + ret = node.getElementsByTagName(refname) if not ret: - error('node has no profile:', node) - ret[0].appendChild(gen.ref(ref, uuid)) - + error('node has no profile ref:', node) + prof_uuid = ret[0].getAttribute('uuidref') + profile = lookup(node.parentNode, prof_uuid) + if not profile: + error("no profile found:", prof_uuid) + if ref_exists(profile, uuid): + return 0 + profile.appendChild(gen.ref(ref, uuid)) + return 1 + +# ensure that uuid is not already in the profile +# return true if uuid is added +def node_found_target_by_dev(gen, lustre, node, devname): + refname = "%s_ref" % "profile" + ret = node.getElementsByTagName(refname) + if not ret: + error('node has no profile ref:', node) + prof_uuid = ret[0].getAttribute('uuidref') + profile = lookup(node.parentNode, prof_uuid) + if not profile: + error("no profile found:", prof_uuid) + + osd_list = lustre.getElementsByTagName('osd') + + for osd in osd_list: + obd_dev = osd.getElementsByTagName('dev') + if obd_dev and obd_dev[0].getAttribute('dev') == devname: + for ost in lustre.getElementsByTagName('ost'): + active_ret = ost.getElementsByTagName('active_ref') + if active_ret[0].getAttribute('uuidref') == osd.getAttribute('uuid'): + return ost.getAttribute('uuid') + + mdsdev_list = lustre.getElementsByTagName('mdsdev') + + for mdsdev in mdsdev_list: + obd_dev = mdsdev.getElementsByTagName('dev') + if obd_dev and obd_dev[0].getAttribute('dev') == devname: + for mds in lustre.getElementsByTagName('mds'): + active_ret = mds.getElementsByTagName('active_ref') + if active_ret[0].getAttribute('uuidref') == mdsdev.getAttribute('uuid'): + return mds.getAttribute('uuid') + + return "" + def get_attr(dom_node, attr, default=""): v = dom_node.getAttribute(attr) if v: @@ -383,24 +858,50 @@ def get_attr(dom_node, attr, default=""): ############################################################ # Top level commands # +def set_node_options(gen, node, options): + if options.router: + node.setAttribute('router', '1') + if options.timeout: + gen.addElement(node, "timeout", get_option(options, 'timeout')) + if options.upcall: + default_upcall = get_option(options, 'upcall') + else: + default_upcall = '' + if default_upcall or options.lustre_upcall: + if options.lustre_upcall: + gen.addElement(node, 'lustreUpcall', options.lustre_upcall) + else: + gen.addElement(node, 'lustreUpcall', default_upcall) + if default_upcall or options.portals_upcall: + if options.portals_upcall: + gen.addElement(node, 'portalsUpcall', options.portals_upcall) + else: + gen.addElement(node, 'portalsUpcall', default_upcall) + if options.ptldebug: + gen.addElement(node, "ptldebug", get_option(options, 'ptldebug')) + if options.subsystem: + gen.addElement(node, "subsystem", get_option(options, 'subsystem')) + return node + def do_add_node(gen, lustre, options, node_name): uuid = new_uuid(node_name) - node = gen.node(node_name, uuid) - node_add_profile(gen, node, 'ldlm', ldlm_uuid) - if options.has_key('router'): - node.setAttribute('router', '1') - node_add_profile(gen, node, "ptlrouter", 'PTLROUTER_UUID') + prof_name = new_name("PROFILE_" + node_name) + prof_uuid = new_uuid(prof_name) + profile = gen.profile(prof_name, prof_uuid) + node = gen.node(node_name, uuid, prof_uuid) lustre.appendChild(node) + lustre.appendChild(profile) + + node_add_profile(gen, node, 'ldlm', ldlm_uuid) + set_node_options(gen, node, options) + return node - -def add_node(gen, lustre, options, args): - """ create a node with a network config """ - if len(args) > 1: - usage() - node_name = options['node'] +def add_node(gen, lustre, options): + """ create a node with a network config """ + node_name = get_option(options, 'node') ret = findByName(lustre, node_name, "node") if ret: print "Node:", node_name, "exists." @@ -408,26 +909,23 @@ def add_node(gen, lustre, options, args): do_add_node(gen, lustre, options, node_name) -def add_net(gen, lustre, options, args): +def add_net(gen, lustre, options): """ create a node with a network config """ - if len(args) < 2: - usage() - - node_name = options['node'] - nid = args[0] - net_type = args[1] - port = 0 - tcpbuf = 0 - - if net_type == 'tcp': - if len(args) > 2: - port = int(args[2]) - else: - port = DEFAULT_PORT - if options.has_key('tcpbuf'): - tcpbuf = int(options['tcpbuf']) + + node_name = get_option(options, 'node') + nid = get_option(options, 'nid') + cluster_id = get_option(options, 'cluster_id') + hostaddr = get_option(options, 'hostaddr') + net_type = get_option(options, 'nettype') + + if net_type in ('tcp',): + port = get_option_int(options, 'port') + tcpbuf = get_option_int(options, 'tcpbuf') + irq_aff = get_option_int(options, 'irq_affinity') elif net_type in ('elan', 'gm'): port = 0 + tcpbuf = 0 + irq_aff = 0 else: print "Unknown net_type: ", net_type sys.exit(2) @@ -437,285 +935,534 @@ def add_net(gen, lustre, options, args): node = do_add_node(gen, lustre, options, node_name) else: node = ret + set_node_options(gen, node, options) + net_name = new_name('NET_'+ node_name +'_'+ net_type) net_uuid = new_uuid(net_name) - node.appendChild(gen.network(net_name, net_uuid, nid, net_type, port, tcpbuf)) + node.appendChild(gen.network(net_name, net_uuid, nid, cluster_id, net_type, + hostaddr, port, tcpbuf, irq_aff)) node_add_profile(gen, node, "network", net_uuid) -def add_route(gen, lustre, options, args): +def add_route(gen, lustre, options): """ create a node with a network config """ - if len(args) < 3: - usage() - - node_name = options['node'] - net_type= args[0] - gw = args[1] - lo = args[2] - hi = '' - if len(args) > 3: - hi = args[3] + node_name = get_option(options, 'node') + gw_net_type = get_option(options, 'nettype') + gw = get_option(options, 'gw') + gw_cluster_id = get_option(options, 'gateway_cluster_id') + tgt_cluster_id = get_option(options, 'target_cluster_id') + lo = get_option(options, 'lo') + hi = get_option(options, 'hi') + if not hi: + hi = lo node = findByName(lustre, node_name, "node") if not node: error (node_name, " not found.") - - netlist = node.getElementsByTagName('network') - for net in netlist: - if get_attr(net, 'type') == net_type: - rlist = net.getElementsByTagName('route_tbl') - if len(rlist) > 0: - rtbl = rlist[0] - else: - rtbl = gen.addElement(net, 'route_tbl') - rtbl.appendChild(gen.route(lo, hi)) - -def add_mds(gen, lustre, options, args): - if len(args) < 1: - usage() - - if options.has_key('node'): - node_name = options['node'] + rlist = node.getElementsByTagName('routetbl') + if len(rlist) > 0: + rtbl = rlist[0] else: - error("--mds requires a --node argument") - - mds_name = new_name(options['mds']) - devname = args[0] - if len(args) > 1: - size = args[1] + rtbl_name = new_name("RTBL_" + node_name) + rtbl_uuid = new_uuid(rtbl_name) + rtbl = gen.routetbl(rtbl_name, rtbl_uuid) + node.appendChild(rtbl) + node_add_profile(gen, node, "routetbl", rtbl_uuid) + rtbl.appendChild(gen.route(gw_net_type, gw, gw_cluster_id, tgt_cluster_id, + lo, hi)) + + +def add_mds(gen, lustre, options): + node_name = get_option(options, 'node') + mds_name = get_option(options, 'mds') + lmv_name = get_option(options, 'lmv') + mdd_name = new_name("MDD_" + mds_name +"_" + node_name) + mdd_uuid = new_uuid(mdd_name) + + lmv_uuid = "" + if lmv_name: + lmv = findByName(lustre, lmv_name, "lmv") + if not lmv: + error('add_mds:', '"' + lmv_name + '"', "lmv element not found.") + lmv_uuid = name2uuid(lustre, lmv_name, fatal=0) + + mds_uuid = name2uuid(lustre, mds_name, 'mds', fatal=0) + if not mds_uuid: + mds_uuid = new_uuid(mds_name) + mds = gen.mds(mds_name, mds_uuid, mdd_uuid, options.group) + lustre.appendChild(mds) + if lmv_name: + lmv_add_obd(gen, lmv, mds_uuid) else: - size = 0 + mds = lookup(lustre, mds_uuid) - mdc_name = 'MDC_' + mds_name - mds_uuid = new_uuid(mds_name) - mdc_uuid = new_uuid(mdc_name) + if options.failover: + mds.setAttribute('failover', "1") - node_uuid = name2uuid(lustre, node_name) + devname = get_option(options, 'dev') + backdevname = get_option(options, 'backdev') + size = get_option(options, 'size') + fstype = get_option(options, 'fstype') + backfstype = get_option(options, 'backfstype') + journal_size = get_option(options, 'journal_size') + inode_size = get_option(options, 'inode_size') + nspath = get_option(options, 'nspath') + mkfsoptions = get_option(options, 'mkfsoptions') + mountfsoptions = get_option(options, 'mountfsoptions') + + node_uuid = name2uuid(lustre, node_name, 'node') node = findByName(lustre, node_name, "node") - node_add_profile(gen, node, "mds", mds_uuid) + node_add_profile(gen, node, "mdsdev", mdd_uuid) net_uuid = get_net_uuid(lustre, node_name) if not net_uuid: error("NODE: ", node_name, "not found") + if lmv_name: + mds.appendChild(gen.ref("lmv", lmv_uuid)) + + mdd = gen.mdsdev(mdd_name, mdd_uuid, fstype, devname, + get_format_flag(options), node_uuid, mds_uuid, + size, journal_size, inode_size, nspath, mkfsoptions, + mountfsoptions, backfstype, backdevname, lmv_uuid) + lustre.appendChild(mdd) + + +def add_mgmt(gen, lustre, options): + node_name = get_option(options, 'node') + node_uuid = name2uuid(lustre, node_name, 'node') + mgmt_name = get_option(options, 'mgmt') + if not mgmt_name: + mgmt_name = new_name('MGMT_' + node_name) + mgmt_uuid = name2uuid(lustre, mgmt_name, 'mgmt', fatal=0) + if not mgmt_uuid: + mgmt_uuid = new_uuid(mgmt_name) + mgmt = gen.mgmt(mgmt_name, mgmt_uuid, node_uuid) + lustre.appendChild(mgmt) + else: + mgmt = lookup(lustre, mgmt_uuid) + + node = findByName(lustre, node_name, "node") + node_add_profile(gen, node, 'mgmt', mgmt_uuid) - mds = gen.mds(mds_name, mds_uuid, "extN", devname, get_format_flag(options), - net_uuid, node_uuid, dev_size=size) - mdc = gen.mdc(mdc_name, mdc_uuid, mds_uuid) - lustre.appendChild(mds) - lustre.appendChild(mdc) - +def add_ost(gen, lustre, options): + node_name = get_option(options, 'node') + lovname = get_option(options, 'lov') + osdtype = get_option(options, 'osdtype') -def add_mdc(gen, lustre, options, args): - """ create mtpt on a node """ - if len(args) < 1: - usage() + node_uuid = name2uuid(lustre, node_name, 'node') - if options.has_key('node'): - node_name = options['node'] + if osdtype == 'obdecho': + fstype = '' + backfstype = '' + devname = '' + backdevname = '' + size = 0 + journal_size = '' + inode_size = '' + mkfsoptions = '' + mountfsoptions = '' else: - error("--mdc requires a --node argument") - - mdc_name = args[0] - mdc_uuid = name2uuid(lustre, mdc_name) + devname = get_option(options, 'dev') # can be unset for bluearcs + backdevname = get_option(options, 'backdev') + size = get_option(options, 'size') + fstype = get_option(options, 'fstype') + backfstype = get_option(options, 'backfstype') + journal_size = get_option(options, 'journal_size') + inode_size = get_option(options, 'inode_size') + mkfsoptions = get_option(options, 'mkfsoptions') + mountfsoptions = get_option(options, 'mountfsoptions') + + nspath = get_option(options, 'nspath') + + ostname = get_option(options, 'ost') + if not ostname: + ostname = new_name('OST_'+ node_name) + + osdname = new_name("OSD_" + ostname + "_" + node_name) + osd_uuid = new_uuid(osdname) + + ost_uuid = name2uuid(lustre, ostname, 'ost', fatal=0) + if not ost_uuid: + ost_uuid = get_option(options, 'ostuuid') + if ost_uuid: + if lookup(lustre, ost_uuid): + error("Duplicate OST UUID:", ost_uuid) + else: + ost_uuid = new_uuid(ostname) - node = findByName(lustre, node_name, "node") - if not node: - error('node:', node_name, "not found.") - node_add_profile(gen, node, "mdc", mdc_uuid) + ost = gen.ost(ostname, ost_uuid, osd_uuid, options.group) + lustre.appendChild(ost) + else: + ost = lookup(lustre, ost_uuid) + if lovname: + lov = findByName(lustre, lovname, "lov") + if not lov: + error('add_ost:', '"'+lovname+'"', "lov element not found.") + lov_add_obd(gen, lustre, lov, ost_uuid, options) -def add_ost(gen, lustre, options, args): - lovname = '' - obdtype = 'obdfilter' - devname = '' - size = 0 - fstype = 'extN' - - if options.has_key('node'): - node_name = options['node'] - else: - error("--ost requires a --node argument") + if options.failover: + ost.setAttribute('failover', "1") - if options.has_key('lov'): - lovname = options['lov'] - if options.has_key('obdtype'): - obdtype = options['obdtype'] + osd = gen.osd(osdname, osd_uuid, fstype, osdtype, devname, + get_format_flag(options), ost_uuid, node_uuid, size, + journal_size, inode_size, nspath, mkfsoptions, + mountfsoptions, backfstype, backdevname) - if obdtype == 'obdecho': - fstype = '' - else: - if len(args) < 1: - usage() - devname = args[0] - if len(args) > 1: - size = args[1] - - obdname = new_name('OBD_'+ node_name) - oscname = new_name('OSC_'+ node_name) - ostname = new_name('OST_'+ node_name) - obd_uuid = new_uuid(obdname) - ost_uuid = new_uuid(ostname) - osc_uuid = new_uuid(oscname) + node = findByName(lustre, node_name, "node") - net_uuid = get_net_uuid(lustre, node_name) - if not net_uuid: - error("NODE: ", node_name, "not found") - - obd = gen.obd(obdname, obd_uuid, fstype, obdtype, devname, get_format_flag(options), size) - ost = gen.ost(ostname, ost_uuid, obd_uuid, net_uuid) - osc = gen.osc(oscname, osc_uuid, obd_uuid, ost_uuid) - +## if node_add_profile(gen, node, 'oss', oss_uuid): +## ossname = 'OSS' +## oss_uuid = new_uuid(ossname) +## oss = gen.oss(ossname, oss_uuid) +## lustre.appendChild(oss) + + node_add_profile(gen, node, 'osd', osd_uuid) + lustre.appendChild(osd) + +def del_ost(gen, lustre, options): + ostname = get_option(options, 'ost') + if not ostname: + raise OptionError("del_ost: --ost requires a ") + ost = findByName(lustre, ostname, "ost") + if not ost: + error('del_ost: ', 'Unable to find ', ostname) + ost_uuid = name2uuid(lustre, ostname, fatal=0) + if not ost_uuid: + error('del_ost: ', 'Unable to find uuid for ', ostname) + lovname = get_option(options, 'lov') if lovname: lov = findByName(lustre, lovname, "lov") if not lov: - error("LOV:", lovname, "not found.") - lov_add_osc(gen, lov, osc_uuid) + error('del_ost:', '"'+lovname+'"', "lov element not found.") + lov_del_obd(gen, lustre, lov, ost_uuid, options) + # if the user specified a speficic LOV don't delete the OST itself + return + + # remove OSD references from all LOVs + for n in lustre.getElementsByTagName('lov'): + lov_del_obd(gen, lustre, n, ost_uuid, options) + if not options.migrate: + return + # delete the OSDs + for osd in lustre.getElementsByTagName('osd'): + if ref_exists(osd, ost_uuid): + osd_uuid = osd.getAttribute('uuid') + # delete all profile references to this OSD + for profile in lustre.getElementsByTagName('profile'): + for osd_ref in profile.getElementsByTagName('osd_ref'): + if osd_uuid == osd_ref.getAttribute('uuidref'): + profile.removeChild(osd_ref) + lustre.removeChild(osd) + + # delete the OST + lustre.removeChild(ost) + +def add_cmobd(gen, lustre, options): + node_name = get_option(options, 'node') + name = get_option(options, 'cmobd') + uuid = new_uuid(name) + + real_name = get_option(options, 'master_dev') + cache_name = get_option(options, 'cache_dev') node = findByName(lustre, node_name, "node") - node_add_profile(gen, node, 'obd', obd_uuid) - node_add_profile(gen, node, 'ost', ost_uuid) - - lustre.appendChild(obd) - lustre.appendChild(osc) - lustre.appendChild(ost) - - -# this is generally only used by llecho.sh -def add_osc(gen, lustre, options, args): - """ add the osc to the profile for this node. """ - if len(args) < 1: - usage() - osc_name = args[0] - if options.has_key('node'): - node_name = options['node'] - else: - error("--osc requires a --node argument") - osc_uuid = name2uuid(lustre, osc_name) + node_add_profile(gen, node, "cmobd", uuid) + real_uuid = node_found_target_by_dev(gen, lustre, node, real_name) + cache_uuid = node_found_target_by_dev(gen, lustre, node, cache_name) + if not real_uuid: + panic("add_cmobd", "can not find real_uuid") + if not cache_uuid: + panic("add_cmobd", "can not find cache_uuid") + cmobd = gen.cmobd(name, uuid, real_uuid, cache_uuid) + lustre.appendChild(cmobd) + +def add_cobd(gen, lustre, options): + node_name = get_option(options, 'node') + name = get_option(options, 'cobd') + uuid = new_uuid(name) + + real_name = get_option(options, 'real_obd') + cache_name = get_option(options, 'cache_obd') + + real_uuid = name2uuid(lustre, real_name, tag='lov', fatal=0) + cache_uuid = name2uuid(lustre, cache_name, tag='lov', fatal=0) + + if real_uuid: + node = lookup(lustre, real_uuid) + rets = node.getElementsByTagName('lov_tgt') + for ret in rets: + ost_uuid = ret.getAttribute('uuidref') + ost_node = lookup(lustre, ost_uuid) + ret = ost_node.getElementsByTagName('active_ref') + if ret: + osd_uuid = ret[0].getAttribute('uuidref') + osd_node = lookup(lustre, osd_uuid) + gen.addElement(osd_node, 'cachetype', 'master') + + if cache_uuid: + node = lookup(lustre, cache_uuid) + rets = node.getElementsByTagName('lov_tgt') + for ret in rets: + ost_uuid = ret.getAttribute('uuidref') + ost_node = lookup(lustre, ost_uuid) + ret = ost_node.getElementsByTagName('active_ref') + if ret: + osd_uuid = ret[0].getAttribute('uuidref') + osd_node = lookup(lustre, osd_uuid) + gen.addElement(osd_node, 'cachetype', 'cache') + + if not real_uuid or not cache_uuid: + real_uuid = name2uuid(lustre,real_name, tag='mds') + cache_uuid = name2uuid(lustre,cache_name, tag='mds') + if real_uuid: + mds_node = lookup(lustre, real_uuid) + ret = mds_node.getElementsByTagName('active_ref') + if ret: + mdsdev_uuid = ret[0].getAttribute('uuidref') + mdsdev_node = lookup(lustre, mdsdev_uuid) + gen.addElement(mdsdev_node, 'cachetype', 'master') + if cache_uuid: + mds_node = lookup(lustre, cache_uuid) + ret = mds_node.getElementsByTagName('active_ref') + if ret: + mdsdev_uuid = ret[0].getAttribute('uuidref') + mdsdev_node = lookup(lustre, mdsdev_uuid) + gen.addElement(mdsdev_node, 'cachetype', 'cache') + node = findByName(lustre, node_name, "node") - node_add_profile(gen, node, 'osc', osc_uuid) + cobd = gen.cobd(name, uuid, real_uuid, cache_uuid) + lustre.appendChild(cobd) + + +def add_echo_client(gen, lustre, options): + """ add an echo client to the profile for this node. """ + node_name = get_option(options, 'node') + lov_name = get_option(options, 'ost') + + node = findByName(lustre, node_name, 'node') + + echoname = new_name('ECHO_'+ node_name) + echo_uuid = new_uuid(echoname) + node_add_profile(gen, node, 'echoclient', echo_uuid) + + lov_uuid = name2uuid(lustre, lov_name, tag='lov', fatal=0) + if not lov_uuid: + lov_uuid = name2uuid(lustre, lov_name, tag='ost', fatal=1) + + echo = gen.echo_client(echoname, echo_uuid, lov_uuid) + lustre.appendChild(echo) -def add_lov(gen, lustre, options, args): +def add_lov(gen, lustre, options): """ create a lov """ - if len(args) < 4: - usage() - - name = options['lov'] - mds_name = args[0] - stripe_sz = args[1] - stripe_off = args[2] - pattern = args[3] + + lmv_name = get_option(options, 'lmv') + lov_orig = get_option(options, 'lov') + name = new_name(lov_orig) + if name != lov_orig: + warning("name:", lov_orig, "already used. using:", name) + + mds_name = get_option(options, 'mds') + if not mds_name: + if not lmv_name: + error("LOV: MDS or LMV must be specified."); + + stripe_sz = get_option_int(options, 'stripe_sz') + stripe_cnt = get_option_int(options, 'stripe_cnt') + pattern = get_option_int(options, 'stripe_pattern') uuid = new_uuid(name) ret = findByName(lustre, name, "lov") if ret: error("LOV: ", name, " already exists.") - mds_uuid = name2uuid(lustre, mds_name) + if not mds_name: + mds_uuid = name2uuid(lustre, lmv_name, 'lmv') + else: + mds_uuid = name2uuid(lustre, mds_name, 'mds') - node = mds2node(lustre, mds_name) - node_add_profile(gen, node, "lov", uuid) - lov = gen.lov(name, uuid, mds_uuid, stripe_sz, stripe_off, pattern) + lov = gen.lov(name, uuid, mds_uuid, stripe_sz, stripe_cnt, pattern) lustre.appendChild(lov) + # add an lovconfig entry to the active mdsdev profile + lovconfig_name = new_name('LVCFG_' + name) + lovconfig_uuid = new_uuid(lovconfig_name) + if mds_name: + mds = findByName(lustre, mds_name, "mds") + mds.appendChild(gen.ref("lovconfig", lovconfig_uuid)) + if lmv_name: + lmv = findByName(lustre, lmv_name, "lmv") + lmv.appendChild(gen.ref("lovconfig", lovconfig_uuid)) + lovconfig = gen.lovconfig(lovconfig_name, lovconfig_uuid, uuid) + lustre.appendChild(lovconfig) + +def add_default_lov(gen, lustre, mds_name, lov_name): + """ create a default lov """ + + stripe_sz = DEFAULT_STRIPE_SZ + stripe_cnt = DEFAULT_STRIPE_CNT + pattern = DEFAULT_STRIPE_PATTERN + uuid = new_uuid(lov_name) + + ret = findByName(lustre, lov_name, "lov") + if ret: + error("LOV: ", lov_name, " already exists.") -def add_mtpt(gen, lustre, options, args): - """ create mtpt on a node """ - if len(args) < 3: - usage() + mds_uuid = name2uuid(lustre, mds_name, 'mds') + lov = gen.lov(lov_name, uuid, mds_uuid, stripe_sz, stripe_cnt, pattern) + lustre.appendChild(lov) + + # add an lovconfig entry to the active mdsdev profile + lovconfig_name = new_name('LVCFG_' + lov_name) + lovconfig_uuid = new_uuid(lovconfig_name) + mds = findByName(lustre, mds_name) + mds.appendChild(gen.ref("lovconfig", lovconfig_uuid)) + lovconfig = gen.lovconfig(lovconfig_name, lovconfig_uuid, uuid) + lustre.appendChild(lovconfig) + +def add_lmv(gen, lustre, options): + """ create a lmv """ - if options.has_key('node'): - node_name = options['node'] + lmv_orig = get_option(options, 'lmv') + name = new_name(lmv_orig) + if name != lmv_orig: + warning("name:", lmv_orig, "already used. using:", name) + + uuid = new_uuid(name) + ret = findByName(lustre, name, "lmv") + if ret: + error("LMV: ", name, " already exists.") + + lmv = gen.lmv(name, uuid) + lustre.appendChild(lmv) + +def new_filesystem(gen, lustre, mds_uuid, obd_uuid, mgmt_uuid): + fs_name = new_name("FS_fsname") + fs_uuid = new_uuid(fs_name) + cobd = lookup(lustre, mds_uuid) + #SHOULD appendChild filesystem to real mds not cobd + ret = cobd.getElementsByTagName("cacheobd_ref") + if ret: + cacheobd_uuid = ret[0].getAttribute('uuidref') + cacheobd = lookup(lustre, cacheobd_uuid) + cacheobd.appendChild(gen.ref("filesystem", fs_uuid)) + ret = cobd.getElementsByTagName("realobd_ref") + if ret: + realobd_uuid = ret[0].getAttribute('uuidref') + realobd = lookup(lustre, realobd_uuid) + realobd.appendChild(gen.ref("filesystem", fs_uuid)) else: - error("--mtpt requires a --node argument") + cobd.appendChild(gen.ref("filesystem", fs_uuid)) + fs = gen.filesystem(fs_name, fs_uuid, mds_uuid, obd_uuid, mgmt_uuid) + lustre.appendChild(fs) + return fs_uuid + +def get_fs_uuid(gen, lustre, mds_name, obd_name, mgmt_name): + mds_uuid = name2uuid(lustre, mds_name, tag='mds', fatal=0) + if not mds_uuid: + mds_uuid = name2uuid(lustre, mds_name, tag='lmv', fatal=0) + if not mds_uuid: + mds_uuid = name2uuid(lustre, mds_name, tag='cobd', fatal=1) + obd_uuid = name2uuid(lustre, obd_name, tag='lov', fatal=0) + if obd_uuid == '': + obd_uuid = name2uuid(lustre, obd_name, tag='cobd') + if mgmt_name: + mgmt_uuid = name2uuid(lustre, mgmt_name, tag='mgmt', fatal=1) + else: + mgmt_uuid = '' + fs_uuid = lookup_filesystem(lustre, mds_uuid, obd_uuid) + if not fs_uuid: + fs_uuid = new_filesystem(gen, lustre, mds_uuid, obd_uuid, mgmt_uuid) + return fs_uuid - path = args[0] - mds_name = args[1] - lov_name = args[2] - mdc_name = 'MDC_' + mds_name +def add_mtpt(gen, lustre, options): + """ create mtpt on a node """ + node_name = get_option(options, 'node') + + path = get_option(options, 'path') + clientoptions = get_option(options, "clientoptions") + fs_name = get_option(options, 'filesystem') + + lov_name = get_option(options, 'lov') + ost_name = get_option(options, 'ost') + mds_name = get_option(options, 'mds') + if mds_name == '': + mds_name = get_option(options, 'lmv') + if mds_name == '': + error("--add mtpt requires either --mds or --lmv.") + if lov_name == '': + if ost_name == '': + error("--add mtpt requires --lov lov_name or --ost ost_name") + else: + warning("use default value for lov, due no --lov lov_name provided") + lov_name = new_name("lov_default") + add_default_lov(gen, lustre, mds_name, lov_name) + ost_uuid = name2uuid(lustre, ost_name, 'ost', fatal=0) + if not ost_uuid: + error('add_mtpt:', '"'+ost_name+'"', "ost element not found.") + lov = findByName(lustre, lov_name, "lov") + lov_add_obd(gen, lustre, lov, ost_uuid, options) + + if fs_name == '': + mgmt_name = get_option(options, 'mgmt') + fs_uuid = get_fs_uuid(gen, lustre, mds_name, lov_name, mgmt_name) + else: + fs_uuid = name2uuid(lustre, fs_name, tag='filesystem') name = new_name('MNT_'+ node_name) ret = findByName(lustre, name, "mountpoint") if ret: + # this can't happen, because new_name creates unique names error("MOUNTPOINT: ", name, " already exists.") - mdc_uuid = name2uuid(lustre, mdc_name) - lov_uuid = name2uuid(lustre, lov_name, tag='lov', fatal=0) - if not lov_uuid: - lov_uuid = name2uuid(lustre, lov_name, tag='osc', fatal=1) - uuid = new_uuid(name) - mtpt = gen.mountpoint(name, uuid, mdc_uuid, lov_uuid, path) + mtpt = gen.mountpoint(name, uuid, fs_uuid, path, clientoptions) node = findByName(lustre, node_name, "node") if not node: - error('node:', node_name, "not found.") + error('node:', node_name, "not found.") node_add_profile(gen, node, "mountpoint", uuid) - node_add_profile(gen, node, "mdc", mdc_uuid) lustre.appendChild(mtpt) +def commit_version(gen, lustre): + update = findLastUpdate(lustre) + if update: + version = int(update.getAttribute("version")) + 1 + else: + version = 1 + + new = gen.update(str(version)) + lustre.appendChild(new) + ############################################################ # Command line processing # -def parse_cmdline(argv): - short_opts = "ho:i:m:" - long_opts = ["ost", "osc", "mtpt", "lov=", "node=", "mds=", "net", "tcpbuf=", - "mdc", "route", "router", "merge=", "format", "reformat", "output=", - "obdtype=", "in=", "help"] - opts = [] - args = [] - options = {} - try: - opts, args = getopt.getopt(argv, short_opts, long_opts) - except getopt.error: - print "invalid opt" - usage() - - for o, a in opts: - if o in ("-h", "--help"): - usage() - if o in ("-o", "--output"): - options['output'] = a - if o == "--ost": - options['ost'] = 1 - if o == "--osc": - options['osc'] = 1 - if o == "--mds": - options['mds'] = a - if o == "--mdc": - options['mdc'] = 1 - if o == "--net": - options['net'] = 1 - if o == "--mtpt": - options['mtpt'] = 1 - if o == "--node": - options['node'] = a - if o == "--route": - options['route'] = 1 - if o == "--router": - options['router'] = 1 - if o == "--lov": - options['lov'] = a - if o in ("-m", "--merge"): - options['merge'] = a - if o == "--obdtype": - options['obdtype'] = a - if o == "--tcpbuf": - options['tcpbuf'] = a - if o == "--format": - options['format'] = 1 - if o == "--reformat": - options['reformat'] = 1 - if o in ("--in" , "-i"): - options['in'] = a - - return options, args +class OptionError (exceptions.Exception): + def __init__(self, args): + self.args = args + +def get_option(options, tag): + """Look for tag in options hash and return the value if set. If not + set, then if return default it is set, otherwise exception.""" + if options.__getattr__(tag) != None: + return options.__getattr__(tag) + else: + raise OptionError("--add %s requires --%s " % (options.add, tag)) +def get_option_int(options, tag): + """Return an integer option. Raise exception if the value is not an int""" + val = get_option(options, tag) + try: + n = int(val) + except ValueError: + raise OptionError("--%s (value must be integer)" % (tag)) + return n # simple class for profiling import time @@ -736,26 +1483,151 @@ class chrono: str = '%s: %g secs' % (msg, d) print str +################################################################# +# function cmdlinesplit used to split cmd line from batch file +# +def cmdlinesplit(cmdline): + + double_quote = re.compile(r'"(([^"\\]|\\.)*)"') + single_quote = re.compile(r"'(.*?)'") + escaped = re.compile(r'\\(.)') + esc_quote = re.compile(r'\\([\\"])') + outside = re.compile(r"""([^\s\\'"]+)""") #" fucking emacs. + + arg_list = [] + i = 0; arg = None + while i < len(cmdline): + c = cmdline[i] + if c == '"': + match = double_quote.match(cmdline, i) + if not match: + print "Unmatched double quote:", cmdline + sys.exit(1) + i = match.end() + if arg is None: arg = esc_quote.sub(r'\1', match.group(1)) + else: arg = arg + esc_quote.sub(r'\1', match.group(1)) + + elif c == "'": + match = single_quote.match(cmdline, i) + if not match: + print "Unmatched single quote:", cmdline + sys.exit(1) + i = match.end() + if arg is None: arg = match.group(1) + else: arg = arg + match.group(1) + + elif c == "\\": + match = escaped.match(cmdline, i) + if not match: + print "Unmatched backslash", cmdline + sys.exit(1) + i = match.end() + if arg is None: arg = match.group(1) + else: arg = arg + match.group(1) + + elif c in string.whitespace: + if arg != None: + arg_list.append(str(arg)) + arg = None + while i < len(cmdline) and cmdline[i] in string.whitespace: + i = i + 1 + else: + match = outside.match(cmdline, i) + assert match + i = match.end() + if arg is None: arg = match.group() + else: arg = arg + match.group() + + if arg != None: arg_list.append(str(arg)) + + return arg_list + ############################################################ # Main # + +def add(devtype, gen, lustre, options): + if devtype == 'net': + add_net(gen, lustre, options) + elif devtype == 'mtpt': + add_mtpt(gen, lustre, options) + elif devtype == 'mds': + add_mds(gen, lustre, options) + elif devtype == 'ost': + add_ost(gen, lustre, options) + elif devtype == 'lov': + add_lov(gen, lustre, options) + elif devtype == 'route': + add_route(gen, lustre, options) + elif devtype == 'node': + add_node(gen, lustre, options) + elif devtype == 'echo_client': + add_echo_client(gen, lustre, options) + elif devtype == 'cobd': + add_cobd(gen, lustre, options) + elif devtype == 'cmobd': + add_cmobd(gen, lustre, options) + elif devtype == 'mgmt': + add_mgmt(gen, lustre, options) + elif devtype == 'lmv': + add_lmv(gen, lustre, options) + else: + error("unknown device type:", devtype) + +def delete(devtype, gen, lustre, options): + if devtype == 'ost': + del_ost(gen, lustre, options) + elif options.delete: + error("delete not supported for device type:", devtype) + elif options.deactivate: + error("deactivate not supported for device type:", devtype) + else: + error("in delete(), but neither .delete nor .deactivate are set. Tell CFS.") + +def commit(gen, lustre): + commit_version(gen, lustre) + +def do_command(gen, lustre, options, args): + if options.add: + add(options.add, gen, lustre, options) + elif options.delete: + delete(options.delete, gen, lustre, options) + elif options.deactivate: + delete(options.deactivate, gen, lustre, options) + elif options.commit: + commit(gen, lustre) + else: + error("Missing command") + def main(): - options, args = parse_cmdline(sys.argv[1:]) + cl = Lustre.Options("lmc", "", lmc_options) + try: + options, args = cl.parse(sys.argv[1:]) + except Lustre.OptionError, e: + panic("lmc", e) + + if len(args) > 0: + panic(string.join(sys.argv), "Unexpected extra arguments on command line: " + string.join(args)) + + if options.reference: + reference() + sys.exit(0) + outFile = '-' - if options.has_key('merge'): - outFile = options['merge'] + if options.merge: + outFile = options.merge if os.access(outFile, os.R_OK): doc = xml.dom.minidom.parse(outFile) else: doc = new_lustre(xml.dom.minidom) - elif options.has_key('in'): - doc = xml.dom.minidom.parse(options['in']) + elif options.input: + doc = xml.dom.minidom.parse(options.input) else: doc = new_lustre(xml.dom.minidom) - if options.has_key('output'): - outFile = options['output'] + if options.output: + outFile = options.output lustre = doc.documentElement init_names(lustre) @@ -764,34 +1636,34 @@ def main(): sys.exit(1) gen = GenConfig(doc) - if options.has_key('ost'): - add_ost(gen, lustre, options, args) - elif options.has_key('osc'): - add_osc(gen, lustre, options, args) - elif options.has_key('mtpt'): - add_mtpt(gen, lustre, options, args) - elif options.has_key('mds'): - add_mds(gen, lustre, options, args) - elif options.has_key('mdc'): - add_mdc(gen, lustre, options, args) - elif options.has_key('net'): - add_net(gen, lustre, options, args) - elif options.has_key('lov'): - add_lov(gen, lustre, options, args) - elif options.has_key('route'): - add_route(gen, lustre, options, args) - elif options.has_key('node'): - add_node(gen, lustre, options, args) + + if options.batch: + fp = open(options.batch) + batchCommands = fp.readlines() + fp.close() + for cmd in batchCommands: + try: + options, args = cl.parse(cmdlinesplit(cmd)) + if options.merge or options.input or options.output: + print "The batchfile should not contain --merge, --input or --output." + sys.exit(1) + do_command(gen, lustre, options, args) + except OptionError, e: + panic(cmd, e) + except Lustre.OptionError, e: + panic(cmd, e) else: - print "Missing command" - usage() + try: + do_command(gen, lustre, options, args) + except OptionError, e: + panic(string.join(sys.argv),e) + except Lustre.OptionError, e: + panic("lmc", e) if outFile == '-': - PrettyPrint(doc) + printDoc(doc) else: - PrettyPrint(doc, open(outFile,"w")) + printDoc(doc, open(outFile,"w")) if __name__ == "__main__": main() - -