# # 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 . # # Author: Alina Quereilhac 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 @clsinit_copy class PlanetlabNode(LinuxNode): _rtype = "PlanetlabNode" @classmethod def _register_attributes(cls): cls._remove_attribute("username") ip = Attribute("ip", "PlanetLab host public IP address", flags = Flags.ReadOnly) slicename = Attribute("slice", "PlanetLab slice name", 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) city = Attribute("city", "Constrain location (city) during resource discovery. May use wildcards.", flags = Flags.Filter) 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) cls._register_attribute(ip) cls._register_attribute(slicename) cls._register_attribute(pl_url) cls._register_attribute(pl_ptn) cls._register_attribute(city) cls._register_attribute(country) cls._register_attribute(region) cls._register_attribute(architecture) cls._register_attribute(operating_system) cls._register_attribute(min_reliability) cls._register_attribute(max_reliability) cls._register_attribute(min_bandwidth) cls._register_attribute(max_bandwidth) cls._register_attribute(min_load) cls._register_attribute(max_load) cls._register_attribute(min_cpu) cls._register_attribute(max_cpu) cls._register_attribute(timeframe) def __init__(self, ec, guid): super(PLanetlabNode, self).__init__(ec, guid) self._plapi = None @property def plapi(self): if not self._plapi: slicename = self.get("slice") pl_pass = self.get("password") pl_url = self.get("plcApiUrl") pl_ptn = self.get("plcApiPattern") self._plapi = PLCAPIFactory.get_api(slicename, pl_pass, pl_url, pl_ptn) return self._plapi @property def os(self): if self._os: return self._os if (not self.get("hostname") or not self.get("username")): msg = "Can't resolve OS, insufficient data " self.error(msg) raise RuntimeError, msg (out, err), proc = self.execute("cat /etc/issue", with_lock = True) if err and proc.poll(): msg = "Error detecting OS " self.error(msg, out, err) raise RuntimeError, "%s - %s - %s" %( msg, out, err ) if out.find("Fedora release 12") == 0: self._os = "f12" elif out.find("Fedora release 14") == 0: self._os = "f14" else: msg = "Unsupported OS" self.error(msg, out) raise RuntimeError, "%s - %s " %( msg, out ) return self._os def provision(self): if not self.is_alive(): self._state = ResourceState.FAILED msg = "Deploy failed. Unresponsive node %s" % self.get("hostname") self.error(msg) raise RuntimeError, msg if self.get("cleanProcesses"): self.clean_processes() if self.get("cleanHome"): self.clean_home() self.mkdir(self.node_home) super(PlanetlabNode, self).provision() def deploy(self): if self.state == ResourceState.NEW: try: self.discover() if self.state == ResourceState.DISCOVERED: self.provision() except: self._state = ResourceState.FAILED raise if self.state != ResourceState.PROVISIONED: self.ec.schedule(reschedule_delay, self.deploy) super(PlanetlabNode, self).deploy() def valid_connection(self, guid): # TODO: Validate! return True def clean_processes(self, killer = False): self.info("Cleaning up processes") # Hardcore kill cmd = ("sudo -S killall python tcpdump || /bin/true ; " + "sudo -S killall python tcpdump || /bin/true ; " + "sudo -S kill $(ps -N -T -o pid --no-heading | grep -v $PPID | sort) || /bin/true ; " + "sudo -S killall -u root || /bin/true ; " + "sudo -S killall -u root || /bin/true ; ") out = err = "" (out, err), proc = self.execute(cmd, retry = 1, with_lock = True) def blacklist(self): # TODO!!!! self.warn(" Blacklisting malfunctioning node ") #import util #util.appendBlacklist(self.hostname)