From c5f331fe9ac0a5c6a6ba7ea931b00e15a8ae1d28 Mon Sep 17 00:00:00 2001 From: Pat Pannuto Date: Fri, 10 Aug 2012 16:45:57 -0400 Subject: [PATCH 1/7] Put MNI scripts into a namespace [of sorts] 'Install', 'Reset', and 'Stop' are a bit overloaded.. --- scripts/{add_node.sh => mni_add_node.sh} | 0 scripts/{energy_measurement.py => mni_energy_measurement.py} | 0 scripts/{install.py => mni_install.py} | 0 scripts/{install_quanto.py => mni_install_quanto.py} | 0 scripts/{press_usr.py => mni_press_usr.py} | 0 scripts/{programming.py => mni_programming.py} | 0 scripts/{reset.py => mni_reset.py} | 0 scripts/{reset_quanto.py => mni_reset_quanto.py} | 0 scripts/{serialmode.py => mni_serialmode.py} | 0 scripts/{stop.py => mni_stop.py} | 0 10 files changed, 0 insertions(+), 0 deletions(-) rename scripts/{add_node.sh => mni_add_node.sh} (100%) rename scripts/{energy_measurement.py => mni_energy_measurement.py} (100%) rename scripts/{install.py => mni_install.py} (100%) rename scripts/{install_quanto.py => mni_install_quanto.py} (100%) rename scripts/{press_usr.py => mni_press_usr.py} (100%) rename scripts/{programming.py => mni_programming.py} (100%) rename scripts/{reset.py => mni_reset.py} (100%) rename scripts/{reset_quanto.py => mni_reset_quanto.py} (100%) rename scripts/{serialmode.py => mni_serialmode.py} (100%) rename scripts/{stop.py => mni_stop.py} (100%) diff --git a/scripts/add_node.sh b/scripts/mni_add_node.sh similarity index 100% rename from scripts/add_node.sh rename to scripts/mni_add_node.sh diff --git a/scripts/energy_measurement.py b/scripts/mni_energy_measurement.py similarity index 100% rename from scripts/energy_measurement.py rename to scripts/mni_energy_measurement.py diff --git a/scripts/install.py b/scripts/mni_install.py similarity index 100% rename from scripts/install.py rename to scripts/mni_install.py diff --git a/scripts/install_quanto.py b/scripts/mni_install_quanto.py similarity index 100% rename from scripts/install_quanto.py rename to scripts/mni_install_quanto.py diff --git a/scripts/press_usr.py b/scripts/mni_press_usr.py similarity index 100% rename from scripts/press_usr.py rename to scripts/mni_press_usr.py diff --git a/scripts/programming.py b/scripts/mni_programming.py similarity index 100% rename from scripts/programming.py rename to scripts/mni_programming.py diff --git a/scripts/reset.py b/scripts/mni_reset.py similarity index 100% rename from scripts/reset.py rename to scripts/mni_reset.py diff --git a/scripts/reset_quanto.py b/scripts/mni_reset_quanto.py similarity index 100% rename from scripts/reset_quanto.py rename to scripts/mni_reset_quanto.py diff --git a/scripts/serialmode.py b/scripts/mni_serialmode.py similarity index 100% rename from scripts/serialmode.py rename to scripts/mni_serialmode.py diff --git a/scripts/stop.py b/scripts/mni_stop.py similarity index 100% rename from scripts/stop.py rename to scripts/mni_stop.py From 7797f6e6bb130843c76c59f31ff18fe5785ba154 Mon Sep 17 00:00:00 2001 From: Pat Pannuto Date: Fri, 10 Aug 2012 16:48:05 -0400 Subject: [PATCH 2/7] Clarify config.ini samples, add Digi sample --- scripts/config.ini.SAMPLE.DIGI_CONNECT | 13 +++++++++++++ scripts/{config.ini => config.ini.SAMPLE.SERIAL} | 0 2 files changed, 13 insertions(+) create mode 100644 scripts/config.ini.SAMPLE.DIGI_CONNECT rename scripts/{config.ini => config.ini.SAMPLE.SERIAL} (100%) diff --git a/scripts/config.ini.SAMPLE.DIGI_CONNECT b/scripts/config.ini.SAMPLE.DIGI_CONNECT new file mode 100644 index 0000000..f766fd3 --- /dev/null +++ b/scripts/config.ini.SAMPLE.DIGI_CONNECT @@ -0,0 +1,13 @@ +[Nodes] +numNodes: 1 +type: QuantoTestbedMote +makeCmd: make epic + +[Node1] +id: 1 +ip: 00-40-9d-3d-6b-0a.eecs.umich.edu +serial: /dev/ttysb00 +installCmd: make epic reinstall,$id digi bsl,$serial +timeoffset: 0 + + diff --git a/scripts/config.ini b/scripts/config.ini.SAMPLE.SERIAL similarity index 100% rename from scripts/config.ini rename to scripts/config.ini.SAMPLE.SERIAL From 559b8cb24f777c356e5cbd9f80a4d14afc1b6b8a Mon Sep 17 00:00:00 2001 From: Pat Pannuto Date: Fri, 10 Aug 2012 17:09:09 -0400 Subject: [PATCH 3/7] Update with new udev fix --- scripts/mni_add_node.sh | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/scripts/mni_add_node.sh b/scripts/mni_add_node.sh index 29e25bc..7e838e6 100755 --- a/scripts/mni_add_node.sh +++ b/scripts/mni_add_node.sh @@ -15,10 +15,33 @@ while true; do done set -x -sudo /usr/bin/dgrp/config/dgrp_cfg_node init -v -v -e never $NODE_NAME $NODE_IP 1 > /dev/null -sudo chgrp dialout "/dev/tty"$NODE_NAME"00" -sudo chmod g+rwx "/dev/tty"$NODE_NAME"00" +sudo /usr/bin/dgrp/config/dgrp_cfg_node init -v -v -e never $NODE_NAME $NODE_IP 1 > /dev/null && sleep 1 set +x +TTY_NAME="/dev/tty/${NODE_NAME}00" +if [ -e "$TTY_NAME" ]; then + if ! [[ -r "$TTY_NAME" && -w "$TTY_NAME" ]]; then + echo "ERR: Current user does not have read/write permissions" + echo "on $TTY_NAME" + echo "Consider fixing your udev rule by appending:" + echo -e '\tGROUP="dialout' + echo "Also ensure that the current user is a member of the" + echo "dailout group (or any other group of your choice)" + echo + read -p "Would you like to fixup $TTY_NAME now? [Y/n]" resp + if [ echo ${resp:0:1} | tr [:lower:] [:upper:] == "N" ]; then + echo "WARN: You will need to fix this before attempting to use this node" + echo "Continuing on..." + else + set -x + sudo chgrp dialout "$TTY_NAME" + sudo chmod g+rwx "$TTY_NAME" + set +x + fi + fi +else + echo "ERR: Device $TTY_NAME was not created" + exit 1 +fi if [ -w "config.ini" ]; then while true; do From a048778308b2450793b520d76d697959ca3a1066 Mon Sep 17 00:00:00 2001 From: Pat Pannuto Date: Fri, 10 Aug 2012 19:27:27 -0400 Subject: [PATCH 4/7] Allow -f to select config file --- scripts/mni_install.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/scripts/mni_install.py b/scripts/mni_install.py index 74316f0..6bcfb76 100755 --- a/scripts/mni_install.py +++ b/scripts/mni_install.py @@ -3,7 +3,13 @@ import sys import optparse -m = mni.MNI() +# Real optparse another day +try: + config = sys.argv[sys.argv.index("-f") + 1] + m = mni.MNI(configFile=config) +except ValueError: + m = mni.MNI() + m.compile() sys.stdout.write("Installing application on nodes: ") From 53412561304022b7940dce2194f8bf43975dc763 Mon Sep 17 00:00:00 2001 From: Pat Pannuto Date: Fri, 10 Aug 2012 19:52:39 -0400 Subject: [PATCH 5/7] Fix running processes check to not always count down 0 < 0.1*totalProcesses... ;) --- mni/mni.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mni/mni.py b/mni/mni.py index 2c875cc..9441949 100644 --- a/mni/mni.py +++ b/mni/mni.py @@ -150,6 +150,9 @@ def install_all(self): if p.isAlive(): runningProcesses.append(p) + if len(runningProcesses) is 0: + break + if len(runningProcesses) < 0.1*totalProcesses: # we have less than 10% of processes left. # give them 10 seconds to finish, or else kill them. From b80e3fbab67b1d751502dc65501bfa82e865a577 Mon Sep 17 00:00:00 2001 From: Pat Pannuto Date: Fri, 10 Aug 2012 19:55:23 -0400 Subject: [PATCH 6/7] Modify Quanto mote to allow name-only configs --- mni/mni.py | 43 +++++++++++++++--------- mni/node.py | 97 ++++++++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 112 insertions(+), 28 deletions(-) diff --git a/mni/mni.py b/mni/mni.py index 9441949..a43adce 100644 --- a/mni/mni.py +++ b/mni/mni.py @@ -1,3 +1,5 @@ +# vim: ts=4 et sw=4 sts=4 + import os import sys import ConfigParser @@ -62,25 +64,36 @@ def __init__(self, configFile="config.ini", addIgnore=False): # Verify presence of required options for current node. if not self.config.has_section(nodeString): raise ConfigParser.NoSectionError, "["+nodeString+"]" - self._verify_required_options(nodeString, attributes) - - # Set required options for the current node. - configuration = {} - for a in attributes: - configuration[a] = self.config.get(nodeString, a) - n = nodeType() configured = False + n = nodeType() try: - n.configure(configuration) + # We allow nodes with flexible configurations (e.g. Quanto's) + # to parse their config section themselves as they do not have + # a fixed list of required sections, rather a set of allowable + # sections. Nodes without an _ex method will default to the + # legacy method + n.configure_ex(nodeString, self.config) configured = True - except KeyError, e: - if addIgnore: - print "Node:", nodeString," is not connected. Adding anyway" - self.nodes.append(n) - else: - # Node does not exist, print an error - print "Node:", nodeString," is not connected. Ignoring" + except AttributeError: + self._verify_required_options(nodeString, attributes) + + # Set required options for the current node. + configuration = {} + for a in attributes: + configuration[a] = self.config.get(nodeString, a) + + try: + n.configure(configuration) + configured = True + except KeyError, e: + if addIgnore: + print "Node:", nodeString," is not connected. Adding anyway" + self.nodes.append(n) + else: + # Node does not exist, print an error + print "Node:", nodeString," is not connected. Ignoring" + if configured: self.nodes.append(n) diff --git a/mni/node.py b/mni/node.py index c89b8c8..ba8dcdf 100644 --- a/mni/node.py +++ b/mni/node.py @@ -1,3 +1,5 @@ +# vim: ts=4 et sw=4 sts=4 + import os import sys import rci @@ -114,6 +116,25 @@ def get_required_attributes(): class QuantoTestbedMote(Node): + # The better way might have been to move this configuration information to + # an external file, but it should be a static property of all Quanto motes, + # and thus an inherent property of being a Quanto. + + DEFAULT_INSTALL_COMMAND = "make epic reinstall,$id digi bsl,$serial" + DEFAULT_TIMEOFFSET = 0 + NODES = { + "rd":"00:40:9d:3d:6c:31", + "re":"00:40:9d:3d:69:ed", + "rg":"00:40:9d:3d:6c:21", + "rk":"00:40:9d:3d:6c:16", + "rl":"00:40:9d:3d:6a:29", + "rm":"00:40:9d:3d:6a:d5", + "rs":"00:40:9d:3d:6a:d1", + "rv":"00:40:9d:3d:6c:20", + "rw":"00:40:9d:38:24:90", + "sb":"00:40:9d:3d:6b:0a" + } + def __init__(self): Node.__init__(self) self.installSuccess = False @@ -122,27 +143,33 @@ def __init__(self): self.alwaysOffStates = [] self.alwaysOnStates = [] - def configure(self, configuration): - Node.configure(self, configuration) + # Propogates KeyError on failure + def get_node_info_by_name(self, name): + # I believe the Digi namespace is solid? + serial = "/dev/tty" + name + "00" - for a in QuantoTestbedMote.get_required_attributes(): - if a not in configuration.keys(): - raise KeyError, "Configuration must include key '%s'"%(a,) - ip = configuration["ip"] - serial = configuration["serial"] - installCmd = configuration["installCmd"] - self.timeoffset = int(configuration["timeoffset"]) + # Will raise KeyError if unknown node + mac = self.NODES[name] + host = mac.replace(":", "-") + ".eecs.umich.edu" + + # Generate a consistent, unique id as a courtesy + k = self.NODES.keys() + k.sort() + id = k.index(name) + 1 + + return id, host, serial + def _verify_config(self, host, serial, installCmd): # check if we can telnet to the IP - self.ip = ip + self.host = host try: t = telnetlib.Telnet() - t.open(ip) + t.open(host) t.read_until("login: ", timeout=1) t.close() except socket.error: - raise ValueError, "ERROR: Could not connect to node with IP %s\n"%(self.ip,) + raise ValueError, "ERROR: Could not connect to node at %s\n"%(self.host,) self.serial = serial if not os.path.exists(self.serial): @@ -152,8 +179,52 @@ def configure(self, configuration): self.installCmd = template.substitute(serial = self.serial, id=self.id) # add the RCI interface - self.rci = rci.RCI(self.ip) + self.rci = rci.RCI(self.host) + + def configure_ex(self, key, config): + if config.has_option(key, "name"): + try: + name = config.get(key, "name") + id, host, serial = self.get_node_info_by_name(name) + + except KeyError: + print "WARN: Error parsing node with name ", name + print " Failing over to default configure path" + print + print "You may need to add this node to the NODES array in the" + print "QuantoTestbedMote class if it is a new node" + raise AttributeError + + # Allow override from config file + host = config.get(key,"ip") if config.has_option(key,"ip") else host + serial = config.get(key,"serial") if config.has_option(key,"serial") else serial + installCmd = config.get(key,"installCmd") if config.has_option(key,"installCmd") else self.DEFAULT_INSTALL_COMMAND + self.timeoffset = config.get(key,"timeoffset") if config.has_option(key,"timeoffset") else self.DEFAULT_TIMEOFFSET + + # Set a unique id as a courtesy as it is common to all Node types + if not config.has_option(key,"id"): + config.set(key, "id", id) + + self.id = config.get(key, "id") + + self._verify_config(host, serial, installCmd) + else: + # Raising AttributeError falls back to standard configure path + raise AttributeError + + def configure(self, configuration): + Node.configure(self, configuration) + + for a in QuantoTestbedMote.get_required_attributes(): + if a not in configuration.keys(): + raise KeyError, "Configuration must include key '%s'"%(a,) + # rename to more appropriate term 'host' as ip or hostname both work + host = configuration["ip"] + serial = configuration["serial"] + installCmd = configuration["installCmd"] + self.timeoffset = int(configuration["timeoffset"]) + self._verify_config(host, serial, installCmd) def install(self): From 8463dde90cd337779b4666166af706e06a1e9721 Mon Sep 17 00:00:00 2001 From: Pat Pannuto Date: Fri, 10 Aug 2012 19:59:22 -0400 Subject: [PATCH 7/7] Point users to node add script XXX: Auto run eventually --- mni/node.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mni/node.py b/mni/node.py index ba8dcdf..1653c43 100644 --- a/mni/node.py +++ b/mni/node.py @@ -173,7 +173,9 @@ def _verify_config(self, host, serial, installCmd): self.serial = serial if not os.path.exists(self.serial): - raise ValueError, "ERROR: Serial port %s does not exist\n"%(self.serial,) + msg = "ERROR: Serial port %s does not exist\n"%(self.serial,) + msg += " If this is a new mote, run mni_add_node" + raise ValueError, msg template = Template(installCmd) self.installCmd = template.substitute(serial = self.serial, id=self.id)