2 # NEPI, a framework to manage network experiments
3 # Copyright (C) 2013 INRIA
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation, either version 3 of the License, or
8 # (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program. If not, see <http://www.gnu.org/licenses/>.
18 # Author: Lucia Guevgeozian Odizzio <lucia.guevgeozian_odizzio@inria.fr>
25 from nepi.util.logger import Logger
28 from sfa.client.sfi import Sfi
29 from sfa.util.xrn import hrn_to_urn
31 log = Logger("SFA API")
32 log.debug("Packages sfa-common or sfa-client not installed.\
33 Could not import sfa.client.sfi or sfa.util.xrn")
35 from nepi.util.sfarspec_proc import SfaRSpecProcessing
39 API for quering the SFA service.
41 def __init__(self, sfi_user, sfi_auth, sfi_registry, sfi_sm, private_key,
44 self.sfi_user = sfi_user
45 self.sfi_auth = sfi_auth
46 self.sfi_registry = sfi_registry
48 self.private_key = private_key
49 self.timeout = timeout
51 self._blacklist = set()
52 self._reserved = set()
53 self._resources_cache = None
54 self._already_cached = False
55 self._log = Logger("SFA API")
57 self.rspec_proc = SfaRSpecProcessing()
58 self.lock = threading.Lock()
60 def _sfi_exec_method(self, command, slicename=None, rspec=None, urn=None):
64 if command in ['describe', 'delete', 'allocate', 'provision']:
66 raise TypeError("The slice hrn is expected for this method %s" % command)
67 if command == 'allocate' and not rspec:
68 raise TypeError("RSpec is expected for this method %s" % command)
70 if command == 'allocate':
71 args_list = [slicename, rspec]
72 elif command == 'delete':
73 args_list = [slicename, urn]
74 else: args_list = [slicename, '-o', '/tmp/rspec_output']
76 elif command == 'resources':
77 args_list = ['-o', '/tmp/rspec_output']
79 else: raise TypeError("Sfi method not supported")
81 self.api.options.timeout = self.timeout
82 self.api.options.raw = None
83 self.api.options.user = self.sfi_user
84 self.api.options.auth = self.sfi_auth
85 self.api.options.registry = self.sfi_registry
86 self.api.options.sm = self.sfi_sm
87 self.api.options.user_private_key = self.private_key
89 self.api.command = command
90 self.api.command_parser = self.api.create_parser_command(self.api.command)
91 (command_options, command_args) = self.api.command_parser.parse_args(args_list)
92 self.api.command_options = command_options
93 self.api.read_config()
96 self.api.dispatch(command, command_options, command_args)
97 with open("/tmp/rspec_output.rspec", "r") as result_file:
98 result = result_file.read()
101 def get_resources_info(self):
103 Get all resources and its attributes from aggregate.
106 rspec_slice = self._sfi_exec_method('resources')
108 raise RuntimeError("Fail to list resources")
110 self._resources_cache = self.rspec_proc.parse_sfa_rspec(rspec_slice)
111 self._already_cached = True
112 return self._resources_cache
114 def get_resources_hrn(self, resources=None):
116 Get list of resources hrn, without the resource info.
119 if not self._already_cached:
120 resources = self.get_resources_info()['resource']
122 resources = self._resources_cache['resource']
124 component_tohrn = dict()
125 for resource in resources:
126 hrn = resource['hrn'].replace('\\', '')
127 component_tohrn[resource['component_name']] = hrn
129 return component_tohrn
131 def get_slice_resources(self, slicename):
133 Get resources and info from slice.
137 rspec_slice = self._sfi_exec_method('describe', slicename)
139 raise RuntimeError("Fail to allocate resource for slice %s" % slicename)
141 result = self.rspec_proc.parse_sfa_rspec(rspec_slice)
145 def add_resource_to_slice(self, slicename, resource_hrn, leases=None):
147 Get the list of resources' urn, build the rspec string and call the allocate
148 and provision method.
150 resources_hrn_new = list()
151 resource_parts = resource_hrn.split('.')
152 resource_hrn = '.'.join(resource_parts[:2]) + '.' + '\\.'.join(resource_parts[2:])
153 resources_hrn_new.append(resource_hrn)
155 slice_resources = self.get_slice_resources(slicename)['resource']
159 slice_resources_hrn = self.get_resources_hrn(slice_resources)
160 for s_hrn_key, s_hrn_value in slice_resources_hrn.iteritems():
161 s_parts = s_hrn_value.split('.')
162 s_hrn = '.'.join(s_parts[:2]) + '.' + '\\.'.join(s_parts[2:])
163 resources_hrn_new.append(s_hrn)
165 resources_urn = self._get_resources_urn(resources_hrn_new)
166 rspec = self.rspec_proc.build_sfa_rspec(slicename, resources_urn, leases)
167 f = open("/tmp/rspec_input.rspec", "w")
172 if not os.path.getsize("/tmp/rspec_input.rspec") > 0:
173 raise RuntimeError("Fail to create rspec file to allocate resource in slice %s" % slicename)
176 self._sfi_exec_method('allocate', slicename, "/tmp/rspec_input.rspec")
178 raise RuntimeError("Fail to allocate resource for slice %s" % slicename)
180 self._sfi_exec_method('provision', slicename)
182 raise RuntimeError("Fail to provision resource for slice %s" % slicename)
185 def remove_resource_from_slice(self, slicename, resource_hrn, leases=None):
187 Get the list of resources' urn, build the rspec string and call the allocate
188 and provision method.
190 resource_urn = self._get_resources_urn([resource_hrn]).pop()
192 self._sfi_exec_method('delete', slicename, urn=resource_urn)
194 raise RuntimeError("Fail to delete resource for slice %s" % slicename)
198 def _get_resources_urn(self, resources_hrn):
200 Builds list of resources' urn based on hrn.
202 resources_urn = list()
204 for resource in resources_hrn:
205 resources_urn.append(hrn_to_urn(resource, 'node'))
209 def blacklist_resource(self, resource_hrn):
210 self._blacklist.add(resource_hrn)
212 def blacklisted(self):
213 return self._blacklist
215 def unblacklist_resource(self, resource_hrn):
216 del self._blacklist[resource_hrn]
218 def reserve_resource(self, resource_hrn):
219 self._reserved.add(resource_hrn)
222 return self._reserved
225 class SFAAPIFactory(object):
227 API Factory to manage a map of SFAAPI instances as key-value pairs, it
228 instanciate a single instance per key. The key represents the same SFA,
232 _lock = threading.Lock()
237 def get_api(cls, sfi_user, sfi_auth, sfi_registry, sfi_sm, private_key,
240 if sfi_user and sfi_sm:
241 key = cls.make_key(sfi_user, sfi_sm)
243 api = cls._apis.get(key)
246 api = SFAAPI(sfi_user, sfi_auth, sfi_registry, sfi_sm, private_key,
255 def make_key(cls, *args):
256 skey = "".join(map(str, args))
257 return hashlib.md5(skey).hexdigest()