Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 31 additions & 15 deletions mni/mni.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# vim: ts=4 et sw=4 sts=4

import os
import sys
import ConfigParser
Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -150,6 +163,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.
Expand Down
101 changes: 87 additions & 14 deletions mni/node.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# vim: ts=4 et sw=4 sts=4

import os
import sys
import rci
Expand Down Expand Up @@ -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
Expand All @@ -122,38 +143,90 @@ 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):
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)

# 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):

Expand Down
13 changes: 13 additions & 0 deletions scripts/config.ini.SAMPLE.DIGI_CONNECT
Original file line number Diff line number Diff line change
@@ -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


File renamed without changes.
29 changes: 26 additions & 3 deletions scripts/add_node.sh → scripts/mni_add_node.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
File renamed without changes.
8 changes: 7 additions & 1 deletion scripts/install.py → scripts/mni_install.py
Original file line number Diff line number Diff line change
Expand Up @@ -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: ")
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.