applied the except and raise fixers to the master branch to close the gap with py3
[nepi.git] / src / nepi / resources / planetlab / plcapi.py
index 15008d1..d2970e8 100644 (file)
@@ -1,25 +1,25 @@
-"""
-    NEPI, a framework to manage network experiments
-    Copyright (C) 2013 INRIA
-
-    This program is free software: you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation, either version 3 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program.  If not, see <http://www.gnu.org/licenses/>.
-
-"""
+#
+#    NEPI, a framework to manage network experiments
+#    Copyright (C) 2013 INRIA
+#
+#    This program is free software: you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License version 2 as
+#    published by the Free Software Foundation;
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU General Public License for more details.
+#
+#    You should have received a copy of the GNU General Public License
+#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+#
+# Author: Alina Quereilhac <alina.quereilhac@inria.fr>
 
 import functools
 import hashlib
 import socket
+import os
 import time
 import threading
 import xmlrpclib
@@ -135,15 +135,15 @@ class PLCAPI(object):
      
     _required_methods = set()
 
-    def __init__(self, username = None, password = None, session_key = None, 
-            proxy = None,
-            hostname = "www.planet-lab.eu",
-            urlpattern = "https://%(hostname)s:443/PLCAPI/",
+    def __init__(self, username, password, hostname, urlpattern, ec, proxy, session_key = None, 
             local_peer = "PLE"):
 
         self._blacklist = set()
         self._reserved = set()
         self._nodes_cache = None
+        self._already_cached = False
+        self._ecobj = ec
+        self.count = 1 
 
         if session_key is not None:
             self.auth = dict(AuthMethod='session', session=session_key)
@@ -174,7 +174,11 @@ class PLCAPI(object):
             self._proxy_transport = lambda : None
         
         self.threadlocal = threading.local()
-    
+
+        # Load blacklist from file
+        if self._ecobj.get_global('planetlab::Node', 'persist_blacklist'):
+            self._set_blacklist()
+
     @property
     def api(self):
         # Cannot reuse same proxy in all threads, py2.7 is not threadsafe
@@ -191,6 +195,7 @@ class PLCAPI(object):
             return self.api
         
     def test(self):
+        # TODO: Use nepi utils Logger instead of warning!!
         import warnings
         
         # validate XMLRPC server checking supported API calls
@@ -207,10 +212,20 @@ class PLCAPI(object):
         try:
             # test authorization
             network_types = _retry(self.mcapi.GetNetworkTypes)(self.auth)
-        except (xmlrpclib.ProtocolError, xmlrpclib.Fault),e:
+        except (xmlrpclib.ProtocolError, xmlrpclib.Fault) as e:
             warnings.warn(str(e))
         
         return True
+
+    def _set_blacklist(self):
+        nepi_home = os.path.join(os.path.expanduser("~"), ".nepi")
+        plblacklist_file = os.path.join(nepi_home, "plblacklist.txt")
+        with open(plblacklist_file, 'r') as f:
+            hosts_tobl = f.read().splitlines()
+            if hosts_tobl:
+                nodes_id = self.get_nodes(hosts_tobl, ['node_id'])
+                for node_id in nodes_id:
+                    self._blacklist.add(node_id['node_id'])
     
     @property
     def network_types(self):
@@ -269,7 +284,7 @@ class PLCAPI(object):
                 * plain : boolean, use plain bootstrapfs image if set (for tests)  
         """
         if not isinstance(node, (str, int, long)):
-            raise ValueError, "Node must be either a non-unicode string or an int"
+            raise ValueError("Node must be either a non-unicode string or an int")
         return _retry(self.mcapi.GetNodeFlavour)(self.auth, node)
     
     def get_nodes(self, node_id_or_name = None, fields = None, **kw):
@@ -340,8 +355,12 @@ class PLCAPI(object):
             filters.update(kw)
 
             if not filters and not fieldstuple:
-                if not self._nodes_cache:
+                if not self._nodes_cache and not self._already_cached:
+                    self._already_cached = True
                     self._nodes_cache = _retry(self.mcapi.GetNodes)(self.auth)
+                elif not self._nodes_cache:
+                    while not self._nodes_cache:
+                        time.sleep(10)
                 return self._nodes_cache
 
             return _retry(self.mcapi.GetNodes)(self.auth, filters, *fieldstuple)
@@ -409,6 +428,9 @@ class PLCAPI(object):
     def update_slice(self, slice_id_or_name, **kw):
         return _retry(self.mcapi.UpdateSlice)(self.auth, slice_id_or_name, kw)
 
+    def delete_slice_node(self, slice_id_or_name, node_id_or_hostname):
+        return _retry(self.mcapi.DeleteSliceFromNodes)(self.auth, slice_id_or_name, node_id_or_hostname)
+
     def start_multicall(self):
         self.threadlocal.mc = xmlrpclib.MultiCall(self.mcapi)
     
@@ -420,8 +442,8 @@ class PLCAPI(object):
     def get_slice_nodes(self, slicename):
         return self.get_slices(slicename, ['node_ids'])[0]['node_ids']
 
-    def add_slice_nodes(self, slicename, nodes = None):
-        self.update_slice(slicename, nodes = nodes)
+    def add_slice_nodes(self, slicename, nodes):
+        self.update_slice(slicename, nodes=nodes)
 
     def get_node_info(self, node_id):
         self.start_multicall()
@@ -450,24 +472,44 @@ class PLCAPI(object):
         else:
             return None
 
-    def blacklist_host(self, hostname):
-        self._blacklist.add(hostname)
+    def blacklist_host(self, node_id):
+        self._blacklist.add(node_id)
 
     def blacklisted(self):
-        return self._blacklist
+        return self._blacklist 
 
-    def unblacklist_host(self, hostname):
-        del self._blacklist[hostname]
+    def unblacklist_host(self, node_id):
+        del self._blacklist[node_id]
 
-    def reserve_host(self, hostname):
-        self._reserved.add(hostname)
+    def reserve_host(self, node_id):
+        self._reserved.add(node_id)
 
     def reserved(self):
         return self._reserved
 
-    def unreserve_host(self, hostname):
-        del self._reserved[hostname]
-
+    def unreserve_host(self, node_id):
+        del self._reserved[node_id]
+
+    def release(self):
+        self.count -= 1
+        if self.count == 0:
+            blacklist = self._blacklist
+            self._blacklist = set()
+            self._reserved = set()
+            if self._ecobj.get_global('PlanetlabNode', 'persist_blacklist'):
+                if blacklist:
+                    to_blacklist = list()
+                    hostnames = self.get_nodes(list(blacklist), ['hostname'])
+                    for hostname in hostnames:
+                        to_blacklist.append(hostname['hostname'])
+    
+                    nepi_home = os.path.join(os.path.expanduser("~"), ".nepi")
+                    plblacklist_file = os.path.join(nepi_home, "plblacklist.txt")
+    
+                    with open(plblacklist_file, 'w') as f:
+                        for host in to_blacklist:
+                            f.write("%s\n" % host)
+    
 
 class PLCAPIFactory(object):
     """ 
@@ -482,13 +524,12 @@ class PLCAPIFactory(object):
     _apis = dict()
 
     @classmethod 
-    def get_api(cls, slicename, pl_pass, pl_host,
-            pl_ptn = "https://%(hostname)s:443/PLCAPI/",
-            proxy = None):
+    def get_api(cls, pl_user, pl_pass, pl_host,
+            pl_ptn, ec, proxy = None):
         """ Get existing PLCAPI instance
 
-        :param slicename: Planelab slice name
-        :type slicename: str
+        :param pl_user: Planelab user name (used for web login)
+        :type pl_user: str
         :param pl_pass: Planetlab password (used for web login)
         :type pl_pass: str
         :param pl_host: Planetlab registry host (e.g. "www.planet-lab.eu")
@@ -498,23 +539,24 @@ class PLCAPIFactory(object):
         :param proxy: Proxy service url
         :type pl_ptn: str
         """
-        if slice and pl_pass and pl_host:
-            key = cls._make_key(slicename, pl_host)
+        if pl_user and pl_pass and pl_host:
+            key = cls._make_key(pl_user, pl_host)
             with cls._lock:
                 api = cls._apis.get(key)
                 if not api:
-                    api = cls.create_api(slicename, pl_pass, pl_host, pl_ptn, proxy)
+                    api = cls.create_api(pl_user, pl_pass, pl_host, pl_ptn, ec, proxy)
+                else:
+                    api.count += 1
                 return api
         return None
 
     @classmethod 
-    def create_api(cls, slicename, pl_pass, pl_host,
-            pl_ptn = "https://%(hostname)s:443/PLCAPI/",
-            proxy = None):
+    def create_api(cls, pl_user, pl_pass, pl_host,
+            pl_ptn, ec, proxy = None):
         """ Create an PLCAPI instance
 
-        :param slicename: Planelab slice name
-        :type slicename: str
+        :param pl_user: Planelab user name (used for web login)
+        :type pl_user: str
         :param pl_pass: Planetlab password (used for web login)
         :type pl_pass: str
         :param pl_host: Planetlab registry host (e.g. "www.planet-lab.eu")
@@ -524,14 +566,9 @@ class PLCAPIFactory(object):
         :param proxy: Proxy service url
         :type pl_ptn: str
         """
-        api = PLCAPI(
-            username = slicename,
-            password = pl_pass,
-            hostname = pl_host,
-            urlpattern = pl_ptn,
-            proxy = proxy
-        )
-        key = cls._make_key(slicename, pl_host)
+        api = PLCAPI(username = pl_user, password = pl_pass, hostname = pl_host,
+            urlpattern = pl_ptn, ec = ec, proxy = proxy)
+        key = cls._make_key(pl_user, pl_host)
         cls._apis[key] = api
         return api
 
@@ -546,3 +583,4 @@ class PLCAPIFactory(object):
         skey = "".join(map(str, args))
         return hashlib.md5(skey).hexdigest()
 
+