-"""
- 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
+import xmlrpc.client
def _retry(fn):
def rv(*p, **kw):
- for x in xrange(5):
+ for x in range(5):
try:
return fn(*p, **kw)
except (socket.error, IOError, OSError):
_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)
self._url = urlpattern % {'hostname':hostname}
if (proxy is not None):
- import urllib2
- class HTTPSProxyTransport(xmlrpclib.Transport):
+ import urllib.request, urllib.error, urllib.parse
+ class HTTPSProxyTransport(xmlrpc.client.Transport):
def __init__(self, proxy, use_datetime=0):
- opener = urllib2.build_opener(urllib2.ProxyHandler({"https" : proxy}))
- xmlrpclib.Transport.__init__(self, use_datetime)
+ opener = urllib.request.build_opener(urllib.request.ProxyHandler({"https" : proxy}))
+ xmlrpc.client.Transport.__init__(self, use_datetime)
self.opener = opener
def request(self, host, handler, request_body, verbose=0):
- req = urllib2.Request('https://%s%s' % (host, handler), request_body)
+ req = urllib.request.Request('https://%s%s' % (host, handler), request_body)
req.add_header('User-agent', self.user_agent)
self.verbose = verbose
return self.parse_response(self.opener.open(req))
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
- return xmlrpclib.ServerProxy(
+ return xmlrpc.client.ServerProxy(
self._url ,
transport = self._proxy_transport(),
allow_none = True)
return self.api
def test(self):
+ # TODO: Use nepi utils Logger instead of warning!!
import warnings
# validate XMLRPC server checking supported API calls
try:
# test authorization
network_types = _retry(self.mcapi.GetNetworkTypes)(self.auth)
- except (xmlrpclib.ProtocolError, xmlrpclib.Fault),e:
+ except (xmlrpc.client.ProtocolError, xmlrpc.client.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):
* nodefamily : string, the nodefamily this node should be based upon
* 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"
+ if not isinstance(node, (str, 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):
filters = filters, peer=None, **kw)
)
else:
- peer_filter = map(name_to_id, peer)
+ peer_filter = [name_to_id(x) for x in peer]
elif peer is None or peer == self._local_peer:
peer_filter = None
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)
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)
+ self.threadlocal.mc = xmlrpc.client.MultiCall(self.mcapi)
def finish_multicall(self):
mc = self.threadlocal.mc
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()
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):
"""
_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")
: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")
: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
skey = "".join(map(str, args))
return hashlib.md5(skey).hexdigest()
+