Fixing discover and provision method
authorLucia Guevgeozian Odizzio <lucia.guevgeozian_odizzio@inria.fr>
Tue, 10 Sep 2013 12:36:12 +0000 (14:36 +0200)
committerLucia Guevgeozian Odizzio <lucia.guevgeozian_odizzio@inria.fr>
Tue, 10 Sep 2013 12:36:12 +0000 (14:36 +0200)
src/nepi/resources/planetlab/node.py

index f71bc5f..55464d3 100644 (file)
 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 # Author: Alina Quereilhac <alina.quereilhac@inria.fr>
+#         Lucia Guevgeozian <lucia.guevgeozian_odizzio@inria.fr>
 
 from nepi.execution.attribute import Attribute, Flags, Types
 from nepi.execution.resource import ResourceManager, clsinit_copy, ResourceState, \
         reschedule_delay
 from nepi.resources.linux.node import LinuxNode
 from nepi.resources.planetlab.plcapi import PLCAPIFactory 
-from nepi.util.timefuncs import tnow, tdiff, tdiffsec, stabsformat
+from nepi.util.execfuncs import lexec
 
-import subprocess
+from random import randint
+import time
 import threading
 
-# A.Q. GENERAL COMMENTS: This module needs major cleaning up
-#     - Lines should be 80 characters
-#     - Most methods have too many lines and there are no comments or spaces
-#     - There should be only two line breaks between two methods
-#     - Code is too compressed. Hard to read. Add spaces when needed
-#     - In general the code needs to be more subdivided. Use more methods 
-#       with clear names to divide operations (even if you don't reuse the 
-#       methods else where, this will make the code more readable)
-
 @clsinit_copy
 class PlanetlabNode(LinuxNode):
     _rtype = "PlanetlabNode"
 
-    _blacklist = list()
-    _in_provision = list()
-
-    _lock_bl = threading.Lock()
-    _lock_inpro = threading.Lock()
-
-    @classmethod
-    def blacklist(cls):
-        """ Returns the blacklisted nodes
-
-        """
-        return cls._blacklist
-
-    ### A.Q. COMMENT: Why did you wrapped the locks inside methods ?
-    @classmethod
-    def in_provision(cls):
-        """ Returns the nodes that anohter RM is trying to provision
-
-        """
-        return cls._in_provision
+    blacklist = list()
+    provisionlist = list()
 
-    @classmethod
-    def lock_bl(cls):
-        """ Returns the lock for the blacklist
+    lock_blist = threading.Lock()
+    lock_plist = threading.Lock()
 
-        """
-        return cls._lock_bl
-
-    @classmethod
-    def lock_inpro(cls):
-        """ Returns the lock for the provision list
-
-        """
-        return cls._lock_inpro
+    lock_slice = threading.Lock()
 
 
     @classmethod
@@ -81,114 +47,128 @@ class PlanetlabNode(LinuxNode):
         ip = Attribute("ip", "PlanetLab host public IP address",
                 flags = Flags.ReadOnly)
 
-        pl_url = Attribute("plcApiUrl", "URL of PlanetLab PLCAPI host (e.g. www.planet-lab.eu or www.planet-lab.org) ",
-                default = "www.planet-lab.eu",
-                flags = Flags.Credential)
+        pl_url = Attribute("plcApiUrl", "URL of PlanetLab PLCAPI host \
+                    (e.g. www.planet-lab.eu or www.planet-lab.org) ",
+                    default = "www.planet-lab.eu",
+                    flags = Flags.Credential)
 
-        pl_ptn = Attribute("plcApiPattern", "PLC API service regexp pattern (e.g. https://%(hostname)s:443/PLCAPI/ ) ",
-                default = "https://%(hostname)s:443/PLCAPI/",
-                flags = Flags.ExecReadOnly)
+        pl_ptn = Attribute("plcApiPattern", "PLC API service regexp pattern \
+                    (e.g. https://%(hostname)s:443/PLCAPI/ ) ",
+                    default = "https://%(hostname)s:443/PLCAPI/",
+                    flags = Flags.ExecReadOnly)
     
-        pl_user = Attribute("pluser", "PlanetLab account user, as the one to authenticate in the website) ",
-                flags = Flags.Credential)
+        pl_user = Attribute("pluser", "PlanetLab account user, as the one to \
+                    authenticate in the website) ",
+                    flags = Flags.Credential)
 
-        pl_password = Attribute("password", "PlanetLab account password, as the one to authenticate in the website) ",
-                flags = Flags.Credential)
-
-        city = Attribute("city",
-                "Constrain location (city) during resource discovery. May use wildcards.",
-                flags = Flags.Filter)
+        pl_password = Attribute("password", 
+                        "PlanetLab account password, as \
+                        the one to authenticate in the website) ",
+                        flags = Flags.Credential)
 
-        country = Attribute("country",
-                "Constrain location (country) during resource discovery. May use wildcards.",
+        city = Attribute("city", "Constrain location (city) during resource \
+                discovery. May use wildcards.",
                 flags = Flags.Filter)
 
-        region = Attribute("region",
-                "Constrain location (region) during resource discovery. May use wildcards.",
-                flags = Flags.Filter)
-
-        architecture = Attribute("architecture",
-                "Constrain architecture during resource discovery.",
-                type = Types.Enumerate,
-                allowed = ["x86_64",
-                            "i386"],
-                flags = Flags.Filter)
-
-        operating_system = Attribute("operatingSystem",
-                "Constrain operating system during resource discovery.",
-                type = Types.Enumerate,
-                allowed =  ["f8",
-                            "f12",
-                            "f14",
-                            "centos",
-                            "other"],
-                flags = Flags.Filter)
-
-        site = Attribute("site",
-                "Constrain the PlanetLab site this node should reside on.",
+        country = Attribute("country", "Constrain location (country) during \
+                    resource discovery. May use wildcards.",
+                    flags = Flags.Filter)
+
+        region = Attribute("region", "Constrain location (region) during \
+                    resource discovery. May use wildcards.",
+                    flags = Flags.Filter)
+
+        architecture = Attribute("architecture", "Constrain architecture \
+                        during resource discovery.",
+                        type = Types.Enumerate,
+                        allowed = ["x86_64", 
+                                    "i386"],
+                        flags = Flags.Filter)
+
+        operating_system = Attribute("operatingSystem", "Constrain operating \
+                            system during resource discovery.",
+                            type = Types.Enumerate,
+                            allowed =  ["f8",
+                                        "f12",
+                                        "f14",
+                                        "centos",
+                                        "other"],
+                            flags = Flags.Filter)
+
+        site = Attribute("site", "Constrain the PlanetLab site this node \
+                should reside on.",
                 type = Types.Enumerate,
                 allowed = ["PLE",
                             "PLC",
                             "PLJ"],
                 flags = Flags.Filter)
 
-        min_reliability = Attribute("minReliability",
-                "Constrain reliability while picking PlanetLab nodes. Specifies a lower acceptable bound.",
-                type = Types.Double,
-                range = (1, 100),
-                flags = Flags.Filter)
-
-        max_reliability = Attribute("maxReliability",
-                "Constrain reliability while picking PlanetLab nodes. Specifies an upper acceptable bound.",
-                type = Types.Double,
-                range = (1, 100),
-                flags = Flags.Filter)
-
-        min_bandwidth = Attribute("minBandwidth",
-                "Constrain available bandwidth while picking PlanetLab nodes. Specifies a lower acceptable bound.",
-                type = Types.Double,
-                range = (0, 2**31),
-                flags = Flags.Filter)
-
-        max_bandwidth = Attribute("maxBandwidth",
-                "Constrain available bandwidth while picking PlanetLab nodes. Specifies an upper acceptable bound.",
-                type = Types.Double,
-                range = (0, 2**31),
-                flags = Flags.Filter)
-
-        min_load = Attribute("minLoad",
-                "Constrain node load average while picking PlanetLab nodes. Specifies a lower acceptable bound.",
-                type = Types.Double,
-                range = (0, 2**31),
-                flags = Flags.Filter)
-
-        max_load = Attribute("maxLoad",
-                "Constrain node load average while picking PlanetLab nodes. Specifies an upper acceptable bound.",
-                type = Types.Double,
-                range = (0, 2**31),
-                flags = Flags.Filter)
-
-        min_cpu = Attribute("minCpu",
-                "Constrain available cpu time while picking PlanetLab nodes. Specifies a lower acceptable bound.",
-                type = Types.Double,
-                range = (0, 100),
-                flags = Flags.Filter)
-
-        max_cpu = Attribute("maxCpu",
-                "Constrain available cpu time while picking PlanetLab nodes. Specifies an upper acceptable bound.",
-                type = Types.Double,
-                range = (0, 100),
-                flags = Flags.Filter)
-
-        timeframe = Attribute("timeframe",
-                "Past time period in which to check information about the node. Values are year,month, week, latest",
-                default = "week",
-                type = Types.Enumerate,
-                allowed = ["latest",
-                            "week",
-                            "month",
-                            "year"],
-                 flags = Flags.Filter)
+        min_reliability = Attribute("minReliability", "Constrain reliability \
+                            while picking PlanetLab nodes. Specifies a lower \
+                            acceptable bound.",
+                            type = Types.Double,
+                            range = (1, 100),
+                            flags = Flags.Filter)
+
+        max_reliability = Attribute("maxReliability", "Constrain reliability \
+                            while picking PlanetLab nodes. Specifies an upper \
+                            acceptable bound.",
+                            type = Types.Double,
+                            range = (1, 100),
+                            flags = Flags.Filter)
+
+        min_bandwidth = Attribute("minBandwidth", "Constrain available \
+                            bandwidth while picking PlanetLab nodes. \
+                            Specifies a lower acceptable bound.",
+                            type = Types.Double,
+                            range = (0, 2**31),
+                            flags = Flags.Filter)
+
+        max_bandwidth = Attribute("maxBandwidth", "Constrain available \
+                            bandwidth while picking PlanetLab nodes. \
+                            Specifies an upper acceptable bound.",
+                            type = Types.Double,
+                            range = (0, 2**31),
+                            flags = Flags.Filter)
+
+        min_load = Attribute("minLoad", "Constrain node load average while \
+                    picking PlanetLab nodes. Specifies a lower acceptable \
+                    bound.",
+                    type = Types.Double,
+                    range = (0, 2**31),
+                    flags = Flags.Filter)
+
+        max_load = Attribute("maxLoad", "Constrain node load average while \
+                    picking PlanetLab nodes. Specifies an upper acceptable \
+                    bound.",
+                    type = Types.Double,
+                    range = (0, 2**31),
+                    flags = Flags.Filter)
+
+        min_cpu = Attribute("minCpu", "Constrain available cpu time while \
+                    picking PlanetLab nodes. Specifies a lower acceptable \
+                    bound.",
+                    type = Types.Double,
+                    range = (0, 100),
+                    flags = Flags.Filter)
+
+        max_cpu = Attribute("maxCpu", "Constrain available cpu time while \
+                    picking PlanetLab nodes. Specifies an upper acceptable \
+                    bound.",
+                    type = Types.Double,
+                    range = (0, 100),
+                    flags = Flags.Filter)
+
+        timeframe = Attribute("timeframe", "Past time period in which to check\
+                        information about the node. Values are year,month, \
+                        week, latest",
+                        default = "week",
+                        type = Types.Enumerate,
+                        allowed = ["latest",
+                                    "week",
+                                    "month",
+                                    "year"],
+                        flags = Flags.Filter)
 
         cls._register_attribute(ip)
         cls._register_attribute(pl_url)
@@ -232,127 +212,83 @@ class PlanetlabNode(LinuxNode):
         return self._plapi
 
     def discoverl(self):
-        #### A.Q. COMMENT: no need to have methods for the locks and 
-        ##                 other attributes. Please remove.
-        bl = PlanetlabNode.blacklist()
-        inpro = PlanetlabNode.in_provision()
-        lockbl = PlanetlabNode.lock_bl()
-        lockinpro = PlanetlabNode.lock_inpro()
+        """
+        Based on the attributes defined by the user, discover the suitable nodes
+        """
         hostname = self.get("hostname")
-        if hostname: 
-            node_id = self.check_alive_and_active(hostname=hostname)
-            if node_id not in bl and node_id not in inpro:
-                try_other = self.do_ping(node_id)
-                if try_other:
-                    # A.Q. COMMENT: Here you could do 
-                    #
-                    #   with self._lockbl:
-                    #       ...
-                    #
-                    #  Class attributes can still be accesed with 'self'
-                    lockbl.acquire()
-                    bl.append(node_id)
-                    lockbl.release()
-                    msg = "Node %s not alive, pick another node" % hostname
-                    raise RuntimeError, msg
+        if hostname:
+            # the user specified one particular node to be provisioned
+            # check with PLCAPI if it is alvive
+            node_id = self._query_if_alive(hostname=hostname)
+            node_id = node_id.pop()
+
+            # check that the node is not blacklisted or already being provision 
+            # by other RM
+            blist = PlanetlabNode.blacklist
+            plist = PlanetlabNode.provisionlist
+            if node_id not in blist and node_id not in plist:
+                
+                # check that is really alive, by performing ping
+                ping_ok = self._do_ping(node_id)
+                if not ping_ok:
+                    self._blacklist_node(node_id)
+                    self.fail_node_not_alive(hostname)
                 else:
                     self._node_to_provision = node_id
+                    self._put_node_in_provision(node_id)
                     super(PlanetlabNode, self).discover()
-                    #self._discover_time = tnow()
-                    #self._state = ResourceState.DISCOVERED
-                    return node_id
+                
             else:
-                msg = "Node %s not available for provisioning, pick another node" % hostname
-                raise RuntimeError, msg
-                    
+                self.fail_node_not_available(hostname)                
+        
         else:
-            from random import randint
-            nodes = self.filter_based_on_attributes()
-            nodes_alive = self.check_alive_and_active(nodes)
-            print nodes, nodes_alive
-            nodes_inslice = self.check_if_in_slice(nodes_alive)
+            # the user specifies constraints based on attributes, zero, one or 
+            # more nodes can match these constraints 
+            nodes = self._filter_based_on_attributes()
+            nodes_alive = self._query_if_alive(nodes)
+    
+            # nodes that are already part of user's slice have the priority to
+            # provisioned
+            nodes_inslice = self._check_if_in_slice(nodes_alive)
             nodes_not_inslice = list(set(nodes_alive) - set(nodes_inslice))
+            
+            node_id = None
             if nodes_inslice:
-                size = len(nodes_inslice)
-                while size:
-                    size = size - 1
-                    index = randint(0, size)
-                    node_id = nodes_inslice[index]
-                    nodes_inslice[index] = nodes_inslice[size]
-                    if node_id not in bl and node_id not in inpro:
-                        try_other = self.do_ping(node_id)
-                        if not try_other:
-                            lockinpro.acquire()
-                            inpro.append(node_id)
-                            lockinpro.release()
-                            self._node_to_provision = node_id
-
-                            super(PlanetlabNode, self).discover()
-                            #self._discover_time = tnow()
-                            #self._state = ResourceState.DISCOVERED
-                            return node_id
-                        else:
-                            lockbl.acquire()
-                            bl.append(node_id)
-                            lockbl.release()
-
-            if nodes_not_inslice:
-                size = len(nodes_not_inslice)
-                while size:
-                    size = size - 1
-                    index = randint(0, size)
-                    node_id = nodes_not_inslice[index]
-                    nodes_not_inslice[index] = nodes_not_inslice[size]
-                    if node_id not in bl and node_id not in inpro:
-                        try_other = self.do_ping(node_id)
-                        if not try_other:
-                            lockinpro.acquire()
-                            inpro.append(node_id)
-                            lockinpro.release()
-                            self._node_to_provision = node_id
-                            
-                            super(PlanetlabNode, self).discover()
-                            #self._discover_time = tnow()
-                            #self._state = ResourceState.DISCOVERED
-                            return node_id
-                        else:
-                            lockbl.acquire()
-                            bl.append(node_id)
-                            lockbl.release()
-                msg = "Not enough nodes available for provisioning"
-                raise RuntimeError, msg
-
-                    
-
+                node_id = self._choose_random_node(nodes_inslice)
+                
+            if not node_id and nodes_not_inslice:
+                # Either there were no matching nodes in the user's slice, or
+                # the nodes in the slice  were blacklisted or being provisioned
+                # by other RM. Note nodes_not_inslice is never empty
+                node_id = self._choose_random_node(nodes_not_inslice)
+            if not node_id:
+                self.fail_not_enough_nodes()
+
+            self._node_to_provision = node_id
+            super(PlanetlabNode, self).discover()
+            
     def provisionl(self):
-        # A.Q. COMMENT: you can import time on the top
-        import time
-        bl = PlanetlabNode.blacklist()
-        lockbl = PlanetlabNode.lock_bl()
+        """
+        Add node to user's slice after verifing that the node is functioning
+        correctly
+        """
         provision_ok = False
         ssh_ok = False
         proc_ok = False
         timeout = 1200
+
         while not provision_ok:
-            slicename = self.get("username")
             node = self._node_to_provision
-            ip = self.plapi.get_interfaces({'node_id':node}, fields=['ip'])
-            ip = ip[0]['ip']
-            print ip
-
-            self.plapi.add_slice_nodes(slicename, [node])
+            self._set_hostname_attr(node)
+            self._add_node_to_slice(node)
             
+            # check ssh connection
             t = 0 
             while t < timeout and not ssh_ok:
-                # check ssh connection
-
-                # A.Q. COMMENT IMPORTANT! Instead of issuing SSH commands directly use the
-                #    "execute" method inherithed from LinuxNode with blocking = True
-                command = "ssh %s@%s -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no 'echo \'GOOD NODE\''" % (slicename, ip)
-                p = subprocess.Popen(command, shell=True, stdout = subprocess.PIPE, stderr = subprocess.PIPE) 
-                stdout, stderr = p.communicate()
-                if stdout.find("GOOD NODE") < 0:
-                    print t
+
+                cmd = 'echo \'GOOD NODE\''
+                ((out, err), proc) = self.execute(cmd)
+                if out.find("GOOD NODE") < 0:
                     t = t + 60
                     time.sleep(60)
                     continue
@@ -361,45 +297,37 @@ class PlanetlabNode(LinuxNode):
                     continue
 
             if not ssh_ok:
-                with lockbl:
-                    bl.append(node)
-                    print bl
-                    # A.Q. COMMENT: Make method "delete_slice_node" and there 
-                    #               put this code. Repeat this for all calls to plapi.
-                    #               This will make the code cleaner.
-                    self.plapi.delete_slice_node(slicename, [node])
-                    self.discover()
+                # the timeout was reach without establishing ssh connection
+                # the node is blacklisted, deleted from the slice, and a new
+                # node to provision is discovered
+                self._blacklist_node(node)
+                self._delete_node_from_slice(node)
+                self.discover()
                 continue
             
-            # check /proc directory
-            else: # ssh_ok:
-                command = "ssh %s@%s -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no 'mount |grep proc'" % (slicename, ip)
-                p = subprocess.Popen(command, shell=True, stdout = subprocess.PIPE, stderr = subprocess.PIPE)
-                stdout, stderr = p.communicate()
-                if stdout.find("/proc type proc") < 0:
-                    # A.Q. COMMENT: lines 382-384 should go to a method
-                    #       "blacklist_node()"
-                    lockbl.acquire()
-                    bl.append(node)
-                    lockbl.release()
-                    self.plapi.delete_slice_node(slicename, [node])
+            # check /proc directory is mounted (ssh_ok = True)
+            else:
+                cmd = 'mount |grep proc'
+                ((out, err), proc) = self.execute(cmd)
+                if out.find("/proc type proc") < 0:
+                    self._blacklist_node(node)
+                    self._delete_node_from_slice(node)
                     self.discover()
                     continue
             
                 else:
                     provision_ok = True
-                    # set attributes ip, hostname
+                    # set IP attribute
+                    ip = self._get_ip(node)
                     self.set("ip", ip)
-                    hostname = self.plapi.get_nodes(node, ['hostname'])
-                    self.set("hostname", hostname[0]['hostname'])
-                    print self.get("hostname")
             
-        # call provision de linux node?
         super(PlanetlabNode, self).provision()
 
-    def filter_based_on_attributes(self):
-        # Map attributes with tagnames of PL
+    def _filter_based_on_attributes(self):
+        """
+        Retrive the list of nodes ids that match user's constraints 
+        """
+        # Map user's defined attributes with tagnames of PlanetLab
         timeframe = self.get("timeframe")[0]
         attr_to_tags = {
             'city' : 'city',
@@ -420,123 +348,275 @@ class PlanetlabNode(LinuxNode):
         
         nodes_id = []
         filters = {}
+
         for attr_name, attr_obj in self._attrs.iteritems():
             attr_value = self.get(attr_name)
-            if attr_value is not None and attr_obj.flags == 8 and not 'min' in attr_name \
-                and not 'max' in attr_name and attr_name != 'timeframe':
-                attr_tag = attr_to_tags[attr_name]
-                filters['tagname'] = attr_tag
-                filters['value'] = attr_value
-                node_tags = self.plapi.get_node_tags(filters)
-                if node_tags is not None:
-                    if len(nodes_id) == 0:
-                        for node_tag in node_tags:
-                            nodes_id.append(node_tag['node_id'])
-                    else:
-                        nodes_id_tmp = []
-                        for node_tag in node_tags:
-                            if node_tag['node_id'] in nodes_id:
-                                nodes_id_tmp.append(node_tag['node_id'])
-                        if len(nodes_id_tmp):
-                            nodes_id = set(nodes_id) & set(nodes_id_tmp)
-                        else:
-                            self.fail2()
-                else:
-                    self.fail2()
-            elif attr_value is not None and attr_obj.flags == 8 and ('min' or 'max') in attr_name:
+            
+            if attr_value is not None and attr_obj.flags == 8 and \
+                attr_name != 'timeframe':
+        
                 attr_tag = attr_to_tags[attr_name]
                 filters['tagname'] = attr_tag
-                node_tags = self.plapi.get_node_tags(filters)
-                if node_tags is not None:
-                    if len(nodes_id) == 0:
-                        for node_tag in node_tags:
-                            if 'min' in attr_name and node_tag['value'] != 'n/a' and \
-                                float(node_tag['value']) > attr_value:
-                                nodes_id.append(node_tag['node_id'])
-                            elif 'max' in attr_name and node_tag['value'] != 'n/a' and \
-                                float(node_tag['value']) < attr_value:
-                                nodes_id.append(node_tag['node_id'])
-                    else:
-                        nodes_id_tmp = []
-                        for node_tag in node_tags:
-                            if 'min' in attr_name and node_tag['value'] != 'n/a' and \
-                                float(node_tag['value']) > attr_value and \
-                                node_tag['node_id'] in nodes_id:
-                                nodes_id_tmp.append(node_tag['node_id'])
-                            elif 'max' in attr_name and node_tag['value'] != 'n/a' and \
-                                float(node_tag['value']) < attr_value and \
-                                node_tag['node_id'] in nodes_id:
-                                nodes_id_tmp.append(node_tag['node_id'])
-                        if len(nodes_id_tmp):
-                            nodes_id = set(nodes_id) & set(nodes_id_tmp)
-                        else:
-                            self.fail2()
 
+                # filter nodes by fixed constraints e.g. operating system
+                if not 'min' in attr_name and not 'max' in attr_name:
+                    filters['value'] = attr_value
+                    nodes_id = self._filter_by_fixed_attr(filters, nodes_id)
+
+                # filter nodes by range constraints e.g. max bandwidth
+                elif ('min' or 'max') in attr_name:
+                    nodes_id = self._filter_by_range_attr(attr_name, attr_value, filters, nodes_id)
+                
         return nodes_id
                     
-    def check_alive_and_active(self, nodes_id=None, hostname=None):
+
+    def _filter_by_fixed_attr(self, filters, nodes_id):
+        """
+        Query PLCAPI for nodes ids matching fixed attributes defined by the
+        user
+        """
+        node_tags = self.plapi.get_node_tags(filters)
+        if node_tags is not None:
+
+            if len(nodes_id) == 0:
+                # first attribute being matched
+                for node_tag in node_tags:
+                    nodes_id.append(node_tag['node_id'])
+            else:
+                # remove the nodes ids that don't match the new attribute
+                # that is being match
+
+                nodes_id_tmp = []
+                for node_tag in node_tags:
+                    if node_tag['node_id'] in nodes_id:
+                        nodes_id_tmp.append(node_tag['node_id'])
+
+                if len(nodes_id_tmp):
+                    nodes_id = set(nodes_id) & set(nodes_id_tmp)
+                else:
+                    # no node from before match the new constraint
+                    self.fail_discovery()
+        else:
+            # no nodes match the filter applied
+            self.fail_discovery()
+
+        return nodes_id
+
+    def _filter_by_range_attr(self, attr_name, attr_value, filters, nodes_id):
+        """
+        Query PLCAPI for nodes ids matching attributes defined in a certain
+        range, by the user
+        """
+        node_tags = self.plapi.get_node_tags(filters)
+        if node_tags is not None:
+            
+            if len(nodes_id) == 0:
+                # first attribute being matched
+                for node_tag in node_tags:
+                   # check that matches the min or max restriction
+                    if 'min' in attr_name and node_tag['value'] != 'n/a' and \
+                        float(node_tag['value']) > attr_value:
+                        nodes_id.append(node_tag['node_id'])
+
+                    elif 'max' in attr_name and node_tag['value'] != 'n/a' and \
+                        float(node_tag['value']) < attr_value:
+                        nodes_id.append(node_tag['node_id'])
+            else:
+
+                # remove the nodes ids that don't match the new attribute
+                # that is being match
+                nodes_id_tmp = []
+                for node_tag in node_tags:
+
+                    # check that matches the min or max restriction and was a
+                    # matching previous filters
+                    if 'min' in attr_name and node_tag['value'] != 'n/a' and \
+                        float(node_tag['value']) > attr_value and \
+                        node_tag['node_id'] in nodes_id:
+                        nodes_id_tmp.append(node_tag['node_id'])
+
+                    elif 'max' in attr_name and node_tag['value'] != 'n/a' and \
+                        float(node_tag['value']) < attr_value and \
+                        node_tag['node_id'] in nodes_id:
+                        nodes_id_tmp.append(node_tag['node_id'])
+
+                if len(nodes_id_tmp):
+                    nodes_id = set(nodes_id) & set(nodes_id_tmp)
+                else:
+                    # no node from before match the new constraint
+                    self.fail_discovery()
+
+        else: #TODO CHECK
+            # no nodes match the filter applied
+            self.fail_discovery()
+
+        return nodes_id
+        
+    def _query_if_alive(self, nodes_id=None, hostname=None):
+        """
+        Query PLCAPI for nodes that register activity recently, using filters 
+        related to the state of the node, e.g. last time it was contacted
+        """
         if nodes_id is None and hostname is None:
             msg = "Specify nodes_id or hostname"
             raise RuntimeError, msg
+
         if nodes_id is not None and hostname is not None:
             msg = "Specify either nodes_id or hostname"
             raise RuntimeError, msg
 
-        # check node alive
-        import time
+        # define PL filters to check the node is alive
         filters = dict()
         filters['run_level'] = 'boot'
         filters['boot_state'] = 'boot'
         filters['node_type'] = 'regular' 
         filters['>last_contact'] =  int(time.time()) - 2*3600
+
+        # adding node_id or hostname to the filters to check for the particular
+        # node
         if nodes_id:
             filters['node_id'] = list(nodes_id)
-            alive_nodes_id = self.plapi.get_nodes(filters, fields=['node_id'])
+            alive_nodes_id = self._get_nodes_id(filters)
         elif hostname:
             filters['hostname'] = hostname
-            alive_nodes_id = self.plapi.get_nodes(filters, fields=['node_id'])
+            alive_nodes_id = self._get_nodes_id(filters)
+
         if len(alive_nodes_id) == 0:
-            self.fail2()
+            self.fail_discovery()
         else:
             nodes_id = list()
             for node_id in alive_nodes_id:
                 nid = node_id['node_id']
                 nodes_id.append(nid)
+
             return nodes_id
 
+    def _choose_random_node(self, nodes):
+        """
+        From the possible nodes for provision, choose randomly to decrese the
+        probability of different RMs choosing the same node for provision
+        """
+        blist = PlanetlabNode.blacklist
+        plist = PlanetlabNode.provisionlist
+
+        size = len(nodes)
+        while size:
+            size = size - 1
+            index = randint(0, size)
+            node_id = nodes[index]
+            nodes[index] = nodes[size]
+
+            # check the node is not blacklisted or being provision by other RM
+            # and perform ping to check that is really alive
+            if node_id not in blist and node_id not in plist:
+                ping_ok = self._do_ping(node_id)
+                if not ping_ok:
+                    self._blacklist_node(node_id)
+                else:
+                    # discovered node for provision, added to provision list
+                    self._put_node_in_provision(node_id)
+                    return node_id
 
-    def check_if_in_slice(self, nodes_id):
+    def _get_nodes_id(self, filters):
+        return self.plapi.get_nodes(filters, fields=['node_id'])
+
+    def _add_node_to_slice(self, node_id):
+        self.warn(" Adding node to slice ")
+        slicename = self.get("username")
+        with PlanetlabNode.lock_slice:
+            slice_nodes = self.plapi.get_slice_nodes(slicename)
+            slice_nodes.append(node_id)
+            self.plapi.add_slice_nodes(slicename, slice_nodes)
+
+    def _delete_node_from_slice(self, node):
+        self.warn(" Deleting node from slice ")
+        slicename = self.get("username")
+        self.plapi.delete_slice_node(slicename, [node])
+
+    def _set_hostname_attr(self, node):
+        """
+        Query PLCAPI for the hostname of a certain node id and sets the
+        attribute hostname, it will over write the previous value
+        """
+        hostname = self.plapi.get_nodes(node, ['hostname'])
+        self.set("hostname", hostname[0]['hostname'])
+
+    def _check_if_in_slice(self, nodes_id):
+        """
+        Query PLCAPI to find out if any node id from nodes_id is in the user's
+        slice
+        """
         slicename = self.get("username")
         slice_nodes = self.plapi.get_slice_nodes(slicename)
         nodes_inslice = list(set(nodes_id) & set(slice_nodes))
+
         return nodes_inslice
 
-    def do_ping(self, node_id):
-        # A.Q. COMMENT: the execfuncs module in utils will do the local ping for you
-        #               code reuse is good...
+    def _do_ping(self, node_id):
+        """
+        Perform ping command on node's IP matching node id
+        """
+        ping_ok = False
+        ip = self._get_ip(node_id)
+        command = "ping -c2 %s | echo \"PING OK\"" % ip
+
+        (out, err) = lexec(command)
+        if not out.find("PING OK") < 0:
+            ping_ok = True
+
+        return ping_ok 
+
+    def _blacklist_node(self, node):
+        """
+        Add node mal functioning node to blacklist
+        """
+        blist = PlanetlabNode.blacklist
+
+        self.warn(" Blacklisting malfunctioning node ")
+        with PlanetlabNode.lock_blist:
+            blist.append(node)
+
+    def _put_node_in_provision(self, node):
+        """
+        Add node to the list of nodes being provisioned, in order for other RMs
+        to not try to provision the same one again
+        """
+        plist = PlanetlabNode.provisionlist
+
+        self.warn(" Provisioning node ")
+        with PlanetlabNode.lock_plist:
+            plist.append(node)
+
+    def _get_ip(self, node_id):
+        """
+        Query PLCAPI for the IP of a node with certain node id
+        """
         ip = self.plapi.get_interfaces({'node_id':node_id}, fields=['ip'])
         ip = ip[0]['ip']
-        result = subprocess.call(["ping","-c","2",ip],stdout=subprocess.PIPE,stderr=subprocess.PIPE)
-        if result == 0:
-            return False
-        elif result == 1 or result == 2:
-            return True
-
-    # A.Q. Unclear name for method "fail2"
-    def fail2(self):
+        return ip
+
+    def fail_discovery(self):
         self.fail()
         msg = "Discovery failed. No candidates found for node"
         self.error(msg)
         raise RuntimeError, msg
-           
-                        
+
+    def fail_node_not_alive(self, hostname):
+        msg = "Node %s not alive, pick another node" % hostname
+        raise RuntimeError, msg
+    
+    def fail_node_not_available(self, hostname):
+        msg = "Node %s not available for provisioning, pick another \
+                node" % hostname
+        raise RuntimeError, msg
+
+    def fail_not_enough_nodes(self):
+        msg = "Not enough nodes available for provisioning"
+        raise RuntimeError, msg
+
     def valid_connection(self, guid):
         # TODO: Validate!
         return True
 
-#    def blacklist(self):
-#        # TODO!!!!
-#        self.warn(" Blacklisting malfunctioning node ")
-#        #import util
-#        #util.appendBlacklist(self.hostname)