still making both branches closer
[nepi.git] / src / nepi / util / sfaapi.py
index 48dfb48..f460b7e 100644 (file)
@@ -3,9 +3,8 @@
 #    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.
+#    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
@@ -21,6 +20,7 @@ import threading
 import hashlib
 import re
 import os
+import time
 
 from nepi.util.logger import Logger
 
@@ -36,7 +36,7 @@ from nepi.util.sfarspec_proc import SfaRSpecProcessing
 
 class SFAAPI(object):
     """
-    API for quering the SFA service.
+    API for quering the SFA service. It uses Sfi class from the tool sfi client.
     """
     def __init__(self, sfi_user, sfi_auth, sfi_registry, sfi_sm, private_key, ec,
         batch, rtype, timeout):
@@ -46,6 +46,7 @@ class SFAAPI(object):
         self._resources_cache = None
         self._already_cached = False
         self._ec = ec 
+        self.apis = 1
 
         if batch:
             self._testbed_res = rtype
@@ -73,6 +74,10 @@ class SFAAPI(object):
             self._set_blacklist()
 
     def _set_blacklist(self):
+        """
+        Initialize the blacklist with previous nodes blacklisted, in 
+        previous runs.
+        """
         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:
@@ -82,6 +87,12 @@ class SFAAPI(object):
                     self._blacklist.add(host)
 
     def _get_total_res(self):
+        """
+        Get the total amount of resources instanciated using this API,
+        to be able to add them using the same Allocate and Provision
+        call at once. Specially for Wilabt testbed that doesn't allow 
+        to add slivers after the slice already has some.
+        """
         rms = list()
         res_gids = self._ec.resources
         for gid in res_gids:
@@ -90,11 +101,12 @@ class SFAAPI(object):
                 rms.append(rm)
         return rms
 
-    def _sfi_exec_method(self, command, slicename=None, rspec=None, urn=None):
+    def _sfi_exec_method(self, command, slicename=None, rspec=None, urn=None, action=None):
         """
-        Execute sfi method.
+        Execute sfi method, which correspond to SFA call. It can be the following
+        calls: Describe, Delete, Allocate, Provision, ListResources.
         """
-        if command in ['describe', 'delete', 'allocate', 'provision']:
+        if command in ['describe', 'delete', 'allocate', 'provision', 'action']:
             if not slicename:
                 raise TypeError("The slice hrn is expected for this method %s" % command)
             if command == 'allocate' and not rspec:
@@ -106,6 +118,8 @@ class SFAAPI(object):
                 args_list = [slicename]
             if command != 'delete':
                 args_list = args_list + ['-o', '/tmp/rspec_output']
+            if command == 'action':
+                args_list = [slicename, action]
 
         elif command == 'resources':
             args_list = ['-o', '/tmp/rspec_output']
@@ -197,18 +211,17 @@ class SFAAPI(object):
             else: slice_resources = []
             if slice_resources:
                 slice_resources_hrn = self.get_resources_hrn(slice_resources)
-                for s_hrn_key, s_hrn_value in slice_resources_hrn.iteritems():
+                for s_hrn_key, s_hrn_value in slice_resources_hrn.items():
                     s_parts = s_hrn_value.split('.')
                     s_hrn = '.'.join(s_parts[:2]) + '.' + '\\.'.join(s_parts[2:])
                     resources_hrn_new.append(s_hrn)
 
 
             resources_urn = self._get_resources_urn(resources_hrn_new)
-            rspec = self.rspec_proc.build_sfa_rspec(slicename, resources_urn, leases)
-            f = open("/tmp/rspec_input.rspec", "w")
-            f.truncate(0)
-            f.write(rspec)
-            f.close()
+            rspec = self.rspec_proc.build_sfa_rspec(slicename, resources_urn, None, leases)
+            with open("/tmp/rspec_input.rspec", "w") as f:
+                f.truncate(0)
+                f.write(rspec)
             
             if not os.path.getsize("/tmp/rspec_input.rspec") > 0:
                 raise RuntimeError("Fail to create rspec file to allocate resource in slice %s" % slicename)
@@ -229,31 +242,35 @@ class SFAAPI(object):
                     raise RuntimeError("Fail to provision resource for slice %s" % slicename)
                 return True
 
-    def add_resource_to_slice_batch(self, slicename, resource_hrn, leases=None):
+    def add_resource_to_slice_batch(self, slicename, resource_hrn, properties=None, leases=None):
         """
         Method to add all resources together to the slice. Previous deletion of slivers.
+        Specially used for wilabt that doesn't allow to add more resources to the slice
+        after some resources are added. Every sliver have to be deleted and the batch 
+        has to be added at once.
         """
-        # Specially used for wilabt that doesn't allow to add more resources to the slice
-        # after some resources are added. Every sliver have to be deleted and the batch 
-        # has to be added at once.
         self._count += 1
         self._slice_resources_batch.append(resource_hrn)
         resources_hrn_new = list()
         if self._count == len(self._total):
+            check_all_inslice = self._check_all_inslice(self._slice_resources_batch, slicename)
+            if check_all_inslice == True:
+                return True
             for resource_hrn in self._slice_resources_batch:
                 resource_parts = resource_hrn.split('.')
                 resource_hrn = '.'.join(resource_parts[:2]) + '.' + '\\.'.join(resource_parts[2:])
                 resources_hrn_new.append(resource_hrn)
             with self.lock_slice:
-                self._sfi_exec_method('delete', slicename)
+                if check_all_inslice != 0:
+                    self._sfi_exec_method('delete', slicename)
+                    time.sleep(480)
+                
                 # Re implementing urn from hrn because the library sfa-common doesn't work for wilabt
                 resources_urn = self._get_urn(resources_hrn_new)
-                rspec = self.rspec_proc.build_sfa_rspec(slicename, resources_urn, leases)
-
-                f = open("/tmp/rspec_input.rspec", "w")
-                f.truncate(0)
-                f.write(rspec)
-                f.close()
+                rspec = self.rspec_proc.build_sfa_rspec(slicename, resources_urn, properties, leases)
+                with open("/tmp/rspec_input.rspec", "w") as f:
+                    f.truncate(0)
+                    f.write(rspec)
 
                 if not os.path.getsize("/tmp/rspec_input.rspec") > 0:
                     raise RuntimeError("Fail to create rspec file to allocate resources in slice %s" % slicename)
@@ -270,6 +287,7 @@ class SFAAPI(object):
                     try:
                         self._log.debug("Provisioning resources in slice %s" % slicename)
                         self._sfi_exec_method('provision', slicename)
+                        self._sfi_exec_method('action', slicename=slicename, action='geni_start')
                     except:
                         raise RuntimeError("Fail to provision resource for slice %s" % slicename)
                     return True
@@ -279,6 +297,24 @@ class SFAAPI(object):
         else:
             self._log.debug(" Waiting for more nodes to add the batch to the slice ")
 
+    def _check_all_inslice(self, resources_hrn, slicename):
+        slice_res = self.get_slice_resources(slicename)['resource']
+        if slice_res:
+            if len(slice_res[0]['services']) != 0:
+                slice_res_hrn = self.get_resources_hrn(slice_res).values()
+                if self._compare_lists(slice_res_hrn, resources_hrn):
+                    return True
+                else: return len(slice_res_hrn)
+        return 0
+
+    def _compare_lists(self, list1, list2):
+        if len(list1) != len(list2):
+            return False
+        for item in list1:
+            if item not in list2:
+                return False
+        return True
+
     def _get_urn(self, resources_hrn):
         """
         Get urn from hrn.
@@ -295,8 +331,8 @@ class SFAAPI(object):
 
     def remove_resource_from_slice(self, slicename, resource_hrn, leases=None):
         """
-        Get the list of resources' urn, build the rspec string and call the allocate 
-        and provision method.
+        Remove slivers from slice. Currently sfi doesn't support removing particular
+        slivers.
         """
         resource_urn = self._get_resources_urn([resource_hrn]).pop()
         with self.lock_slice:
@@ -309,6 +345,9 @@ class SFAAPI(object):
     def remove_all_from_slice(self, slicename):
         """
         De-allocate and de-provision all slivers of the named slice.
+        Currently sfi doesn't support removing particular
+        slivers, so this method works only for removing every sliver. Setting the
+        resource_hrn parameter is not necessary.
         """
         with self.lock_slice:
             try:
@@ -329,6 +368,10 @@ class SFAAPI(object):
         return resources_urn
 
     def blacklist_resource(self, resource_hrn):
+        """
+        Adding resource_hrn to blacklist, and taking 
+        the resource from the reserved list.
+        """
         with self.lock_blist:
             self._blacklist.add(resource_hrn)
         with self.lock_resv:
@@ -336,15 +379,24 @@ class SFAAPI(object):
                 self._reserved.remove(resource_hrn)
 
     def blacklisted(self, resource_hrn):
+        """
+        Check if the resource is in the blacklist. 
+        """
         with self.lock_blist:
             if resource_hrn in self._blacklist:
                 return True
         return False
 
     def reserve_resource(self, resource_hrn):
+        """
+        Add resource to the reserved list.
+        """
         self._reserved.add(resource_hrn)
 
     def reserved(self, resource_hrn):
+        """
+        Check that the resource in not reserved.
+        """
         with self.lock_resv:
             if resource_hrn in self._reserved:
                 return True
@@ -352,6 +404,33 @@ class SFAAPI(object):
                 self.reserve_resource(resource_hrn)
                 return False
 
+    def release(self):
+        """
+        Remove hosts from the reserved and blacklist lists, and in case
+        the persist attribute is set, it saves the blacklisted hosts
+        in the blacklist file.
+        """
+        self.apis -= 1
+        if self.apis == 0:
+            blacklist = self._blacklist
+            self._blacklist = set()
+            self._reserved = set()
+#            if self._ecobj.get_global('PlanetlabSfaNode', '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 SFAAPIFactory(object):
     """
     API Factory to manage a map of SFAAPI instances as key-value pairs, it
@@ -376,6 +455,8 @@ class SFAAPIFactory(object):
                     api = SFAAPI(sfi_user, sfi_auth, sfi_registry, sfi_sm, private_key,
                         ec, batch, rtype, timeout)
                     cls._apis[key] = api
+                else:
+                    api.apis += 1
 
                 return api