Adding ICN PlanetLab large experiment scenarios
[nepi.git] / src / nepi / testbeds / planetlab / node.py
index eb9a6de..a924e12 100644 (file)
@@ -76,6 +76,7 @@ class Node(object):
     
     DEPENDS_PIDFILE = '/tmp/nepi-depends.pid'
     DEPENDS_LOGFILE = '/tmp/nepi-depends.log'
+
     RPM_FUSION_URL = 'http://download1.rpmfusion.org/free/fedora/rpmfusion-free-release-stable.noarch.rpm'
     RPM_FUSION_URL_F12 = 'http://download1.rpmfusion.org/free/fedora/releases/12/Everything/x86_64/os/rpmfusion-free-release-12-1.noarch.rpm'
     
@@ -92,7 +93,7 @@ class Node(object):
         if not api:
             api = plcapi.PLCAPI()
         self._api = api
-        self._sliceapi = sliceapi
+        self._sliceapi = sliceapi or api
         
         # Attributes
         self.hostname = None
@@ -113,7 +114,7 @@ class Node(object):
         self.maxLoad = None
         self.min_num_external_ifaces = None
         self.max_num_external_ifaces = None
-        self.timeframe = 'm'
+        self._timeframe = 'w'
         
         # Applications and routes add requirements to connected nodes
         self.required_packages = set()
@@ -130,7 +131,8 @@ class Node(object):
         self.ident_path = None
         self.server_key = None
         self.home_path = None
-        self.enable_cleanup = False
+        self.enable_proc_cleanup = False
+        self.enable_home_cleanup = False
         
         # Those are filled when an actual node is allocated
         self._node_id = None
@@ -139,6 +141,27 @@ class Node(object):
 
         # Logging
         self._logger = logging.getLogger('nepi.testbeds.planetlab')
+
+    def set_timeframe(self, timeframe):
+        if timeframe == "latest":
+            self._timeframe = ""
+        elif timeframe == "month":
+            self._timeframe = "m"
+        elif timeframe == "year":
+            self._timeframe = "y"
+        else:
+            self._timeframe = "w"
+
+    def get_timeframe(self):
+        if self._timeframe == "":
+            return "latest"
+        if self._timeframe == "m":
+            return "month"
+        if self._timeframe == "y":
+            return "year"
+        return "week"
+
+    timeframe = property(get_timeframe, set_timeframe)
     
     def _nepi_testbed_environment_setup_get(self):
         command = cStringIO.StringIO()
@@ -153,8 +176,10 @@ class Node(object):
                 for envval in envvals:
                     command.write(' ; export %s=%s' % (envkey, envval))
         return command.getvalue()
+
     def _nepi_testbed_environment_setup_set(self, value):
         pass
+
     _nepi_testbed_environment_setup = property(
         _nepi_testbed_environment_setup_get,
         _nepi_testbed_environment_setup_set)
@@ -178,7 +203,7 @@ class Node(object):
         self._logger.info("Finding candidates for %s", self.make_filter_description())
         
         fields = ('node_id',)
-        replacements = {'timeframe':self.timeframe}
+        replacements = {'timeframe':self._timeframe}
         
         # get initial candidates (no tag filters)
         basefilters = self.build_filters({}, self.BASEFILTERS)
@@ -209,9 +234,9 @@ class Node(object):
             if attr in applicable:
                 tagfilter = rootfilters.copy()
                 tagfilter['tagname'] = tagname % replacements
-                tagfilter[expr % replacements] = getattr(self,attr)
+                tagfilter[expr % replacements] = str(getattr(self,attr))
                 tagfilter['node_id'] = list(candidates)
-                
+              
                 candidates &= set(map(operator.itemgetter('node_id'),
                     self._sliceapi.GetNodeTags(filters=tagfilter, fields=fields)))
 
@@ -262,7 +287,7 @@ class Node(object):
             candidates = set(filter(predicate, candidates))
        
         # make sure hostnames are resolvable
-        hostnames = None
+        hostnames = dict() 
         if candidates:
             self._logger.info("  Found %s candidates. Checking for reachability...", len(candidates))
            
@@ -272,7 +297,7 @@ class Node(object):
 
             def resolvable(node_id):
                 try:
-                    addr = socket.gethostbyname(hostnames[node_id])
+                    addr = server.gethostbyname(hostnames[node_id])
                     return addr is not None
                 except:
                     return False
@@ -345,7 +370,7 @@ class Node(object):
     def rate_nodes(self, nodes):
         rates = collections.defaultdict(int)
         tags = collections.defaultdict(dict)
-        replacements = {'timeframe':self.timeframe}
+        replacements = {'timeframe':self._timeframe}
         tagnames = [ tagname % replacements 
                      for tagname, weight, default in self.RATE_FACTORS ]
        
@@ -380,9 +405,9 @@ class Node(object):
         orig_attrs['max_num_external_ifaces'] = self.max_num_external_ifaces
         self.min_num_external_ifaces = None
         self.max_num_external_ifaces = None
-        self.timeframe = 'm'
+        if not self._timeframe: self._timeframe = 'w'
         
-        replacements = {'timeframe':self.timeframe}
+        replacements = {'timeframe':self._timeframe}
 
         for attr, tag in self.BASEFILTERS.iteritems():
             if tag in info:
@@ -412,7 +437,7 @@ class Node(object):
             orig_attrs['server_key'] = self.server_key
             self.server_key = info['ssh_rsa_key']
         
-        self.hostip = socket.gethostbyname(self.hostname)
+        self.hostip = server.gethostbyname(self.hostname)
         
         try:
             self.__orig_attrs
@@ -453,17 +478,17 @@ class Node(object):
                     RPM_FUSION_URL = self.RPM_FUSION_URL
                     
                 rpmFusion = (
-                  'rpm -q $(rpm -q -p %(RPM_FUSION_URL)s) || sudo -S rpm -i %(RPM_FUSION_URL)s'
+                  'rpm -q rpmfusion-free-release || sudo -S rpm -i %(RPM_FUSION_URL)s'
                 ) % {
                     'RPM_FUSION_URL' : RPM_FUSION_URL
                 }
             else:
                 rpmFusion = ''
-            
+           
             if rpmFusion:
                 (out,err),proc = server.popen_ssh_command(
                     rpmFusion,
-                    host = self.hostname,
+                    host = self.hostip,
                     port = None,
                     user = self.slicename,
                     agent = None,
@@ -475,7 +500,7 @@ class Node(object):
                 if proc.wait():
                     if self.check_bad_host(out,err):
                         self.blacklist()
-                    raise RuntimeError, "Failed to set up application: %s %s" % (out,err,)
+                    raise RuntimeError, "Failed to set up application on host %s: %s %s" % (self.hostname, out,err,)
             
             # Launch p2p yum dependency installer
             self._yum_dependencies.async_setup()
@@ -497,9 +522,11 @@ class Node(object):
                     raise UnresponsiveNodeError, "Unresponsive host %s" % (self.hostname,)
         
         # Ensure the node is clean (no apps running that could interfere with operations)
-        if self.enable_cleanup:
-            self.do_cleanup()
-    
+        if self.enable_proc_cleanup:
+            self.do_proc_cleanup()
+        if self.enable_home_cleanup:
+            self.do_home_cleanup()
+   
     def wait_dependencies(self, pidprobe=1, probe=0.5, pidmax=10, probemax=10):
         # Wait for the p2p installer
         if self._yum_dependencies and not self._installed:
@@ -511,7 +538,7 @@ class Node(object):
         # they have to be created for deployment
         (out,err),proc = server.eintr_retry(server.popen_ssh_command)(
             "echo 'ALIVE'",
-            host = self.hostname,
+            host = self.hostip,
             port = None,
             user = self.slicename,
             agent = None,
@@ -534,8 +561,8 @@ class Node(object):
             return False
     
     def destroy(self):
-        if self.enable_cleanup:
-            self.do_cleanup()
+        if self.enable_proc_cleanup:
+            self.do_proc_cleanup()
     
     def blacklist(self):
         if self._node_id:
@@ -543,12 +570,12 @@ class Node(object):
             import util
             util.appendBlacklist(self.hostname)
     
-    def do_cleanup(self):
+    def do_proc_cleanup(self):
         if self.testbed().recovering:
             # WOW - not now
             return
             
-        self._logger.info("Cleaning up %s", self.hostname)
+        self._logger.info("Cleaning up processes on %s", self.hostname)
         
         cmds = [
             "sudo -S killall python tcpdump || /bin/true ; "
@@ -566,7 +593,7 @@ class Node(object):
                 cmd % {
                     'slicename' : self.slicename ,
                 },
-                host = self.hostname,
+                host = self.hostip,
                 port = None,
                 user = self.slicename,
                 agent = None,
@@ -577,7 +604,36 @@ class Node(object):
                 retry = 3
                 )
             proc.wait()
-    
+     
+    def do_home_cleanup(self):
+        if self.testbed().recovering:
+            # WOW - not now
+            return
+            
+        self._logger.info("Cleaning up home on %s", self.hostname)
+        
+        cmds = [
+            "find . -maxdepth 1  \( -name '.cache' -o -name '.local' -o -name '.config' -o -name 'nepi-*' \) -execdir rm -rf {} + "
+        ]
+
+        for cmd in cmds:
+            (out,err),proc = server.popen_ssh_command(
+                # Some apps need two kills
+                cmd % {
+                    'slicename' : self.slicename ,
+                },
+                host = self.hostip,
+                port = None,
+                user = self.slicename,
+                agent = None,
+                ident_key = self.ident_path,
+                server_key = self.server_key,
+                tty = True, # so that ps -N -T works as advertised...
+                timeout = 60,
+                retry = 3
+                )
+            proc.wait()
+   
     def prepare_dependencies(self):
         # Configure p2p yum dependency installer
         if self.required_packages and not self._installed:
@@ -613,10 +669,10 @@ class Node(object):
         if len(routes) > MAX_VROUTE_ROUTES:
             return 'sliceip'
         
-        vsys_vnet = ipaddr.IPNetwork(vsys_vnet)
+        vsys_vnet = ipaddr.IPv4Network(vsys_vnet)
         for route in routes:
-            dest, prefix, nexthop, metric = route
-            dest = ipaddr.IPNetwork("%s/%d" % (dest,prefix))
+            dest, prefix, nexthop, metric, device = route
+            dest = ipaddr.IPv4Network("%s/%d" % (dest,prefix))
             nexthop = ipaddr.IPAddress(nexthop)
             if dest not in vsys_vnet or nexthop not in vsys_vnet:
                 return 'sliceip'
@@ -624,7 +680,7 @@ class Node(object):
         return 'vroute'
     
     def format_route(self, route, dev, method, action):
-        dest, prefix, nexthop, metric = route
+        dest, prefix, nexthop, metric, device = route
         if method == 'vroute':
             return (
                 "%s %s%s gw %s %s" % (
@@ -747,7 +803,7 @@ class Node(object):
             "( sudo -S bash -c 'cat /vsys/%(method)s.out >&2' & ) ; sudo -S bash -c 'cat > /vsys/%(method)s.in' ; sleep 0.5" % dict(
                 home = server.shell_escape(self.home_path),
                 method = method),
-            host = self.hostname,
+            host = self.hostip,
             port = None,
             user = self.slicename,
             agent = None,