def _measure(): for siteA in host.getAllSites(): if not siteA.hosts.exists(): continue for siteB in host.getAllSites(): if siteA.id > siteB.id: continue choices = list(siteA.hosts.all()) hostA = None while choices and not hostA: hostA = random.choice(choices) if hostA.problems(): choices.remove(hostA) hostA = None if not hostA: continue choices = list(siteB.hosts.all()) if hostA in choices: choices.remove(hostA) hostB = None while choices and not hostB: hostB = random.choice(choices) if hostB.problems(): choices.remove(hostB) hostB = None if not hostB: continue begin = time.time() res = hostA.getProxy().host_ping(hostB.address) end = time.time() logging.logMessage("link measurement", category="link", siteA=siteA.name, siteB=siteB.name, hostA=hostA.name, hostB=hostB.name, result=res) LinkMeasurement.objects.create(siteA=siteA, siteB=siteB, begin=begin, end=end, type=TYPES[0], loss=res["loss"], delayAvg=res.get("rtt_avg", 0.0)/2.0, delayStddev=res.get("rtt_mdev", 0.0)/2.0, measurements=res["transmitted"])
def modify(self, attrs): """ Sets the given attributes to their given values. This method first checks if the change can be made using checkModify() and then executes the attribute changes by calling modify_KEY(VALUE) for each key/value pair in attrs. After calling all these modify_KEY methods, it will save the object. Classes inheriting from this class should only implement the modify_KEY methods and not touch this method. @param attrs: Attributes to change @type attrs: dict """ self.checkModify(attrs) logging.logMessage("modify", category="topology", id=self.id, attrs=attrs) self.setBusy(True) try: for key, value in attrs.iteritems(): if key.startswith("_"): self.setAttribute(key, value) else: getattr(self, "modify_%s" % key)(value) finally: self.setBusy(False) self.save() logging.logMessage("info", category="topology", id=self.id, info=self.info())
def remove(self, recurse=True): self.checkRemove(recurse) logging.logMessage("info", category="topology", id=self.id, info=self.info()) logging.logMessage("remove", category="topology", id=self.id) self.permissions.delete() self.totalUsage.remove() self.delete()
def remove(self): try: logging.logMessage("connection_remove", category="host", host=self.host.name, id=self.num) self.host.getProxy().connection_remove(self.num) except error.UserError, err: if err.code != error.UserError.ENTITY_DOES_NOT_EXIST: self.host.incrementErrors()
def setRole(self, user, role): UserError.check(role in Role.RANKING or not role, code=UserError.INVALID_VALUE, message="Invalid role", data={"roles": Role.RANKING}) self.checkRole(Role.owner) UserError.check(user != currentUser(), code=UserError.INVALID_VALUE, message="Must not set permissions for yourself") logging.logMessage("permission", category="topology", id=self.id, user=user.name, role=role) self.permissions.set(user, role)
def remove(self): UserError.check(self.checkPermissions(), code=UserError.DENIED, message="Not enough permissions") UserError.check(not self.sites.all(), code=UserError.NOT_EMPTY, message="Organization still has sites") UserError.check(not self.users.all(), code=UserError.NOT_EMPTY, message="Organization still has users") logging.logMessage("remove", category="organization", name=self.name) self.totalUsage.remove() self.delete()
def create(el1, el2, attrs=None): if not attrs: attrs = {} UserError.check(el1 != el2, code=UserError.INVALID_CONFIGURATION, message="Cannot connect element with itself") UserError.check(not el1.connection, code=UserError.ALREADY_CONNECTED, message="Element is already connected", data={"element": el1.id}) UserError.check(not el2.connection, code=UserError.ALREADY_CONNECTED, message="Element is already connected", data={"element": el2.id}) UserError.check(el1.CAP_CONNECTABLE, code=UserError.INVALID_VALUE, message="Element can not be connected", data={"element": el1.id}) UserError.check(el2.CAP_CONNECTABLE, code=UserError.INVALID_VALUE, message="Element can not be connected", data={"element": el2.id}) UserError.check(el1.topology == el2.topology, code=UserError.INVALID_VALUE, message="Can only connect elements from same topology") el1.topology.checkRole(Role.manager) con = Connection() con.init(el1.topology, el1, el2, attrs) con.save() con.triggerStart() logging.logMessage("create", category="connection", id=con.id) logging.logMessage("info", category="connection", id=con.id, info=con.info()) return con
def create(owner, attrs=None): if not attrs: attrs = {} top = Topology() top.init(owner=owner, attrs=attrs) logging.logMessage("create", category="topology", id=top.idStr) logging.logMessage("info", category="topology", id=top.idStr, info=top.info()) return top
def remove(self): try: logging.logMessage("connection_remove", category="host", host=self.host.address, id=self.num) self.host.getProxy().connection_remove(self.num) except xmlrpclib.Fault, f: if f.faultCode != fault.UNKNOWN_OBJECT: raise
def createRecord(self, type_, begin, end, measurements, usage): record = UsageRecord() record.init(self, type_, begin, end, measurements, usage) record.save() self.records.add(record) obj = self._object() logging.logMessage("record", category="accounting", type=type_, begin=begin, end=end, measurements=measurements, object=(obj.__class__.__name__.lower(), obj.id), usage=usage.info())
def create(address, site, attrs={}): fault.check(currentUser().hasFlag(Flags.HostsManager), "Not enough permissions") host = Host(address=address, site=site) host.init(attrs) host.save() logging.logMessage("create", category="host", info=host.info()) return host
def create(attrs={}): fault.check(not currentUser().hasFlag(Flags.NoTopologyCreate), "User can not create new topologies") top = Topology() top.init(owner=currentUser()) logging.logMessage("create", category="topology", id=top.id) logging.logMessage("info", category="topology", id=top.id, info=top.info()) return top
def createSite(name, description=""): fault.check(currentUser().hasFlag(Flags.HostsManager), "Not enough permissions") logging.logMessage("create", category="site", name=name, description=description) site = Site(name=name) site.save() site.init({"description": description}) return site
def updateInfo(self): try: self.attrs = self.host.getProxy().element_info(self.num) except error.UserError, err: if err.code == error.UserError.ENTITY_DOES_NOT_EXIST: logging.logMessage("missing element", category="host", host=self.host.name, id=self.num) self.remove() raise
def createOrganization(name, description="", attrs={}): UserError.check(currentUser().hasFlag(Flags.GlobalAdmin), code=UserError.DENIED, message="Not enough permissions") logging.logMessage("create", category="site", name=name, description=description) organization = Organization(name=name) organization.save() attrs.update({"description": description}) organization.init(attrs) return organization
def updateInfo(self): try: self.attrs = self.host.getProxy().element_info(self.num) except xmlrpclib.Fault, f: if f.faultCode == fault.UNKNOWN_OBJECT: logging.logMessage("missing element", category="host", host=self.host.address, id=self.num) self.remove() raise
def create(attrs=None): if not attrs: attrs = {} UserError.check(not currentUser().hasFlag(Flags.NoTopologyCreate), code=UserError.DENIED, message="User can not create new topologies") top = Topology() top.init(owner=currentUser(), attrs=attrs) logging.logMessage("create", category="topology", id=top.id) logging.logMessage("info", category="topology", id=top.id, info=top.info()) return top
def _disown(self): """ called when removal failed. Remove all users from topology. a task will regularly try to remove user-less topologies. :return: """ logging.logMessage("disown", category="topology", id=self.idStr, info=self.info()) self.permissions = [] self.save()
def modifyRole(self, user, role): UserError.check( role in Role.RANKING or not role, code=UserError.INVALID_VALUE, message="Invalid role", data={"roles": Role.RANKING}, ) logging.logMessage("permission", category="topology", id=self.idStr, user=user.name, role=role) self.set_role(user, role)
def modify(self, attrs): logging.logMessage("connection_modify", category="host", host=self.host.address, id=self.num, attrs=attrs) try: self.attrs = self.host.getProxy().connection_modify(self.num, attrs) except xmlrpclib.Fault, f: if f.faultCode == fault.UNKNOWN_OBJECT: logging.logMessage("missing connection", category="host", host=self.host.address, id=self.num) self.remove() raise
def action(self, action, params={}): logging.logMessage("connection_action begin", category="host", host=self.host.address, id=self.num, action=action, params=params) try: res = self.host.getProxy().connection_action(self.num, action, params) except xmlrpclib.Fault, f: if f.faultCode == fault.UNKNOWN_OBJECT: logging.logMessage("missing connection", category="host", host=self.host.address, id=self.num) self.remove() raise
def create(owner, **attrs): top = Topology() top.init(owner=owner, **attrs) logging.logMessage("create", category="topology", id=top.idStr) logging.logMessage("info", category="topology", id=top.idStr, info=top.info()) return top
def _remove(self, recurse=True): self.checkRemove(recurse) logging.logMessage("info", category="topology", id=self.idStr, info=self.info()) logging.logMessage("remove", category="topology", id=self.idStr) if self.id: for el in self.elements: el._remove(recurse=recurse) for con in self.connections: con._remove(recurse=recurse) self.delete()
def modify(self, attrs): logging.logMessage("connection_modify", category="host", host=self.host.name, id=self.num, attrs=attrs) try: self.attrs = self.host.getProxy().connection_modify(self.num, attrs) except error.UserError, err: if err.code == error.UserError.ENTITY_DOES_NOT_EXIST: logging.logMessage("missing connection", category="host", host=self.host.name, id=self.num) self.remove() if err.code == error.UserError.INVALID_STATE: self.updateInfo() raise
def modify(self, attrs): fault.check(currentUser().hasFlag(Flags.HostsManager), "Not enough permissions") logging.logMessage("modify", category="host", name=self.address, attrs=attrs) for key, value in attrs.iteritems(): if key == "site": self.site = getSite(value) elif key == "enabled": self.enabled = value else: fault.raise_("Unknown host attribute: %s" % key, fault.USER_ERROR) self.save()
def _remove(self, recurse=True): self.checkRemove(recurse) logging.logMessage("info", category="topology", id=self.idStr, info=self.info()) logging.logMessage("remove", category="topology", id=self.idStr) if self.id: for el in self.elements: el._remove(recurse=recurse) for con in self.connections: con._remove(recurse=recurse) self.delete() self.totalUsage.remove()
def modifyRole(self, user, role): UserError.check(role in Role.RANKING or not role, code=UserError.INVALID_VALUE, message="Invalid role", data={"roles": Role.RANKING}) logging.logMessage("permission", category="topology", id=self.idStr, user=user.name, role=role) self.set_role(user, role)
def _disown(self): """ called when removal failed. Remove all users from topology. a task will regularly try to remove user-less topologies. :return: """ logging.logMessage("disown", category="topology", id=self.idStr, info=self.info()) self.permissions = [] self.update_or_save(permissions=self.permissions)
def select(site=None, elementTypes=None, connectionTypes=None, networkKinds=None, hostPrefs=None, sitePrefs=None): # STEP 1: limit host choices to what is possible if not sitePrefs: sitePrefs = {} if not hostPrefs: hostPrefs = {} if not networkKinds: networkKinds = [] if not connectionTypes: connectionTypes = [] if not elementTypes: elementTypes = [] all_ = getAll(site=site) if site else getAll() hosts = [] for host in all_: if host.problems(): continue if set(elementTypes) - set(host.elementTypes.keys()): continue if set(connectionTypes) - set(host.connectionTypes.keys()): continue if set(networkKinds) - set(host.getNetworkKinds()): continue hosts.append(host) UserError.check(hosts, code=UserError.INVALID_CONFIGURATION, message="No hosts found for requirements") # any host in hosts can handle the request prefs = dict([(h, 0.0) for h in hosts]) # STEP 2: calculate preferences based on host load els = 0.0 cons = 0.0 for h in hosts: prefs[h] -= h.componentErrors * 25 # discourage hosts with previous errors prefs[h] -= h.getLoad() * 100 # up to -100 points for load els += h.elements.count() cons += h.connections.count() avgEls = els / len(hosts) avgCons = cons / len(hosts) for h in hosts: # between -30 and +30 points for element/connection over-/under-population if avgEls: prefs[h] -= max(-20.0, min(10.0 * (h.elements.count() - avgEls) / avgEls, 20.0)) if avgCons: prefs[h] -= max(-10.0, min(10.0 * (h.connections.count() - avgCons) / avgCons, 10.0)) # STEP 3: calculate preferences based on host location for h in hosts: if h in hostPrefs: prefs[h] += hostPrefs[h] if h.site in sitePrefs: prefs[h] += sitePrefs[h.site] #STEP 4: select the best host hosts.sort(key=lambda h: prefs[h], reverse=True) logging.logMessage("select", category="host", result=hosts[0].name, prefs=dict([(k.name, v) for k, v in prefs.iteritems()]), site=site.name if site else None, element_types=elementTypes, connection_types=connectionTypes, network_types=networkKinds, host_prefs=dict([(k.name, v) for k, v in hostPrefs.iteritems()]), site_prefs=dict([(k.name, v) for k, v in sitePrefs.iteritems()])) return hosts[0]
def remove(self): UserError.check(self.checkPermissions(), code=UserError.DENIED, message="Not enough permissions") UserError.check(not self.elements.all(), code=UserError.NOT_EMPTY, message="Host still has active elements") UserError.check(not self.connections.all(), code=UserError.NOT_EMPTY, message="Host still has active connections") logging.logMessage("remove", category="host", name=self.name) try: for res in self.getProxy().resource_list(): self.getProxy().resource_remove(res["id"]) except: pass self.totalUsage.remove() self.delete()
def create(name, site, attrs=None): if not attrs: attrs = {} user = currentUser() UserError.check(user.hasFlag(Flags.GlobalHostManager) or user.hasFlag(Flags.OrgaHostManager) and user.organization == site.organization, code=UserError.DENIED, message="Not enough permissions") for attr in ["address", "rpcurl"]: UserError.check(attr in attrs.keys(), code=UserError.INVALID_CONFIGURATION, message="Missing attribute for host: %s" % attr) host = Host(name=name, site=site) host.init(attrs) host.save() logging.logMessage("create", category="host", info=host.info()) return host
def action(self, action, params=None): if not params: params = {} logging.logMessage("connection_action begin", category="host", host=self.host.name, id=self.num, action=action, params=params) try: res = self.host.getProxy().connection_action(self.num, action, params) except error.UserError, err: if err.code == error.UserError.ENTITY_DOES_NOT_EXIST: logging.logMessage("missing connection", category="host", host=self.host.name, id=self.num) self.remove() if err.code == error.UserError.INVALID_STATE: self.updateInfo() raise
def createElement(self, type_, parent=None, attrs={}, owner=None): assert not parent or parent.host == self try: el = self.getProxy().element_create(type_, parent.num if parent else None, attrs) except: self.incrementErrors() raise hel = HostElement(host=self, num=el["id"]) hel.usageStatistics = UsageStatistics.objects.create() hel.attrs = el hel.save() logging.logMessage("element_create", category="host", host=self.address, element=el, owner=(owner.__class__.__name__.lower(), owner.id) if owner else None) return hel
def modify(self, attrs): fault.check(currentUser().hasFlag(Flags.HostsManager), "Not enough permissions") logging.logMessage("modify", category="site", name=self.name, attrs=attrs) for key, value in attrs.iteritems(): if key == "description": self.description = value elif key == "location": self.location = value elif key == "geolocation": self.geolocation = value else: fault.raise_("Unknown site attribute: %s" % key, fault.USER_ERROR) self.save()
def ping(siteA, siteB, ignore_missing_site=False): if isinstance(siteA, str): siteA = Site.get(siteA) if isinstance(siteB, str): siteB = Site.get(siteB) if (siteA is None or siteB is None) and ignore_missing_site: return if siteA is None: raise UserError(UserError.ENTITY_DOES_NOT_EXIST, "site does not exist", data={"site": siteA}) if siteB is None: raise UserError(UserError.ENTITY_DOES_NOT_EXIST, "site does not exist", data={"site": siteB}) key = (siteA, siteB) with pingingLock: if key in pinging: return pinging.add(key) try: try: stats = LinkStatistics.objects.get(siteA=siteA, siteB=siteB) except LinkStatistics.DoesNotExist: stats = LinkStatistics(siteA=siteA, siteB=siteB).save() choices = list(siteA.hosts.all()) hostA = None while choices and not hostA: hostA = random.choice(choices) if hostA.problems(): choices.remove(hostA) hostA = None if not hostA: return choices = list(siteB.hosts.all()) if hostA in choices: choices.remove(hostA) hostB = None while choices and not hostB: hostB = random.choice(choices) if hostB.problems(): choices.remove(hostB) hostB = None if not hostB: return begin = time.time() res = hostA.getProxy().host_ping(hostB.address) end = time.time() logging.logMessage("link measurement", category="link", siteA=siteA.name, siteB=siteB.name, hostA=hostA.name, hostB=hostB.name, result=res) stats.add(LinkMeasurement(begin=begin, end=end, loss=res['loss'], delayAvg=res.get("rtt_avg", 0.0)/2.0, delayStddev=res.get("rtt_mdev", 0.0)/2.0, measurements=res["transmitted"])) finally: with pingingLock: pinging.remove(key)
def timeout_task(): now = time.time() setCurrentUser(True) #we are a global admin for top in Topology.objects.filter(timeoutStep=TimeoutStep.INITIAL, timeout__lte=now+config.TOPOLOGY_TIMEOUT_WARNING): try: logging.logMessage("timeout warning", category="topology", id=top.idStr) top.sendNotification(subject="Topology timeout warning: %s" % top, message="The topology %s will time out soon. This means that the topology will be first stopped and afterwards destroyed which will result in data loss. If you still want to use this topology, please log in and renew the topology." % top) top.timeoutStep = TimeoutStep.WARNED top.save() except Exception: handleError() for top in Topology.objects.filter(timeoutStep=TimeoutStep.WARNED, timeout__lte=now): try: logging.logMessage("timeout stop", category="topology", id=top.idStr) top.action_stop() top.timeoutStep = TimeoutStep.STOPPED top.save() except Exception: handleError() for top in Topology.objects.filter(timeoutStep=TimeoutStep.STOPPED, timeout__lte=now-config.TOPOLOGY_TIMEOUT_WARNING): try: logging.logMessage("timeout destroy", category="topology", id=top.idStr) top.action_destroy() top.timeoutStep = TimeoutStep.DESTROYED top.save() except Exception: handleError() for top in Topology.objects.filter(timeoutStep=TimeoutStep.DESTROYED, timeout__lte=now-config.TOPOLOGY_TIMEOUT_REMOVE): try: logging.logMessage("timeout remove", category="topology", id=top.idStr) top.remove() except Exception: handleError()
def _measure(): for siteA in host.getAllSites(): if not siteA.hosts.exists(): continue for siteB in host.getAllSites(): if siteA.id > siteB.id: continue choices = list(siteA.hosts.all()) hostA = None while choices and not hostA: hostA = random.choice(choices) if hostA.problems(): choices.remove(hostA) hostA = None if not hostA: continue choices = list(siteB.hosts.all()) if hostA in choices: choices.remove(hostA) hostB = None while choices and not hostB: hostB = random.choice(choices) if hostB.problems(): choices.remove(hostB) hostB = None if not hostB: continue begin = time.time() res = hostA.getProxy().host_ping(hostB.address) end = time.time() logging.logMessage("link measurement", category="link", siteA=siteA.name, siteB=siteB.name, hostA=hostA.name, hostB=hostB.name, result=res) LinkMeasurement.objects.create( siteA=siteA, siteB=siteB, begin=begin, end=end, type=TYPES[0], loss=res["loss"], delayAvg=res.get("rtt_avg", 0.0) / 2.0, delayStddev=res.get("rtt_mdev", 0.0) / 2.0, measurements=res["transmitted"])
def timeout_task(): topology_config = settings.get_topology_settings() now = time.time() for top in Topology.objects.filter( timeoutStep=TimeoutStep.INITIAL, timeout__lte=now + topology_config[Config.TOPOLOGY_TIMEOUT_WARNING]): try: logging.logMessage("timeout warning", category="topology", id=top.idStr) top.sendNotification( role=Role.owner, subject="Topology timeout warning: %s" % top, message= "The topology %s will time out soon. This means that the topology will be first stopped and afterwards destroyed which will result in data loss. If you still want to use this topology, please log in and renew the topology." % top) top.timeoutStep = TimeoutStep.WARNED top.save() except: wrap_and_handle_current_exception(re_raise=False) for top in Topology.objects.filter(timeoutStep=TimeoutStep.WARNED, timeout__lte=now): try: logging.logMessage("timeout stop", category="topology", id=top.idStr) top.action_stop() top.timeoutStep = TimeoutStep.STOPPED top.save() except: wrap_and_handle_current_exception(re_raise=False) for top in Topology.objects.filter( timeoutStep=TimeoutStep.STOPPED, timeout__lte=now - topology_config[Config.TOPOLOGY_TIMEOUT_WARNING]): try: logging.logMessage("timeout destroy", category="topology", id=top.idStr) top.action_destroy() top.timeoutStep = TimeoutStep.DESTROYED top.save() except: wrap_and_handle_current_exception(re_raise=False) for top in Topology.objects.filter( timeoutStep=TimeoutStep.DESTROYED, timeout__lte=now - topology_config[Config.TOPOLOGY_TIMEOUT_REMOVE]): try: logging.logMessage("timeout remove", category="topology", id=top.idStr) top.remove() except: wrap_and_handle_current_exception(re_raise=False)
def _action(self, action, params): self.checkAction(action) logging.logMessage("action start", category="connection", id=self.id, action=action, params=params) try: if action in self.CUSTOM_ACTIONS: res = getattr(self, "action_%s" % action)(**params) else: mcon = self.mainConnection() assert mcon res = mcon.action(action, params) self.setState(mcon.state, True) except Exception, exc: self.onError(exc) raise
def _removeLocked(self): try: self.reload() except Connection.DoesNotExist: return self.checkRemove() logging.logMessage("info", category="topology", id=self.id, info=self.info()) logging.logMessage("remove", category="topology", id=self.id) self.triggerStop() for el in self.getElements(): el.connection = None el.save() self.elements.clear() #Important, otherwise elements will be deleted self.totalUsage.remove() #not deleting permissions, the object belongs to the topology self.delete()
def _remove(self, recurse=True): self.checkRemove(recurse) logging.logMessage("info", category="topology", id=self.idStr, info=self.info()) logging.logMessage("remove", category="topology", id=self.idStr) if self.id: try: if self.maxState in (StateName.STARTED, StateName.PREPARED): self.action("destroy") for con in self.connections: con._remove() for el in self.elements: el._remove(recurse=recurse) self.delete() except UserError: raise except: self._disown()
def action(self, action, params): """ Executes the action with the given parameters. This method first checks if the action is possible using checkAction() and then executes the action by calling action_ACTION(**params). After calling the action method, it will save the object. Classes inheriting from this class should only implement the action_ACTION method and not touch this method. @param action: Name of the action @type action: str @param params: Parameters for the action @type params: dict """ self.checkAction(action) logging.logMessage("action start", category="topology", id=self.id, action=action, params=params) self.setBusy(True) try: res = getattr(self, "action_%s" % action)(**params) finally: self.setBusy(False) self.save() logging.logMessage("action end", category="topology", id=self.id, action=action, params=params, res=res) logging.logMessage("info", category="topology", id=self.id, info=self.info()) return res
def _modify(self, attrs): self.checkModify(attrs) logging.logMessage("modify", category="connection", id=self.id, attrs=attrs) try: directAttrs = {} for key, value in attrs.iteritems(): if key in self.CUSTOM_ATTRS: getattr(self, "modify_%s" % key)(value) else: if key.startswith("_"): self.setAttribute(key, value) else: directAttrs[key] = value if directAttrs: self.setAttributes(directAttrs) mcon = self.mainConnection() if mcon: mcon.modify(self._remoteAttrs()) except Exception, exc: self.onError(exc) raise
params=params) try: if action in self.CUSTOM_ACTIONS: res = getattr(self, "action_%s" % action)(**params) else: mcon = self.mainConnection() assert mcon res = mcon.action(action, params) self.setState(mcon.state, True) except Exception, exc: self.onError(exc) raise self.save() logging.logMessage("action end", category="connection", id=self.id, action=action, params=params, res=res) logging.logMessage("info", category="connection", id=self.id, info=self.info()) return res def setState(self, state, dummy=None): self.state = state self.save() def checkRemove(self): self.checkRole(Role.manager)
class Connection(PermissionMixin, db.ChangesetMixin, db.ReloadMixin, attributes.Mixin, models.Model): topology = models.ForeignKey(Topology, null=False, related_name="connections") state = models.CharField(max_length=20, validators=[db.nameValidator]) permissions = models.ForeignKey(Permissions, null=False) totalUsage = models.OneToOneField(UsageStatistics, null=True, related_name='+', on_delete=models.SET_NULL) attrs = db.JSONField() #elements: [elements.Element] connection1 = models.ForeignKey(HostConnection, null=True, on_delete=models.SET_NULL, related_name="+") connection2 = models.ForeignKey(HostConnection, null=True, on_delete=models.SET_NULL, related_name="+") connectionElement1 = models.ForeignKey(HostElement, null=True, on_delete=models.SET_NULL, related_name="+") connectionElement2 = models.ForeignKey(HostElement, null=True, on_delete=models.SET_NULL, related_name="+") #host_elements: [host.HostElement] #host_connections: [host.HostConnections] DIRECT_ACTIONS = True DIRECT_ACTIONS_EXCLUDE = [ "start", "stop", "prepare", "destroy", REMOVE_ACTION ] CUSTOM_ACTIONS = {REMOVE_ACTION: [ST_CREATED]} DIRECT_ATTRS = True DIRECT_ATTRS_EXCLUDE = [] CUSTOM_ATTRS = {} DEFAULT_ATTRS = { "emulation": True, "bandwidth_to": 10000, "bandwidth_from": 10000 } DOC = "" class Meta: pass def init(self, topology, el1, el2, attrs=None): if not attrs: attrs = {} self.topology = topology self.permissions = topology.permissions self.attrs = dict(self.DEFAULT_ATTRS) self.state = ST_CREATED self.totalUsage = UsageStatistics.objects.create() self.save() self.elements.add(el1) self.elements.add(el2) self.save() self.modify(attrs) def _saveAttributes(self): pass #disable automatic attribute saving @classmethod def canConnect(cls, el1, el2): return el1.CAP_CONNECTABLE and el2.CAP_CONNECTABLE def upcast(self): return self.reload() def mainConnection(self): return self.connection1 def remoteType(self): return self.mainConnection().type if self.mainConnection( ) else "bridge" def _adaptAttrs(self, attrs): tmp = {} reversed = not self._correctDirection() #@ReservedAssignment for key, value in attrs.iteritems(): if reversed: if key.endswith("_from"): key = key[:-5] + "_to" elif key.endswith("_to"): key = key[:-3] + "_from" tmp[key] = value return tmp def _remoteAttrs(self): caps = getConnectionCapabilities(self.remoteType()) allowed = caps["attrs"].keys() if caps else [] attrs = {} reversed = not self._correctDirection() #@ReservedAssignment for key, value in self.attrs.iteritems(): if key in allowed: attrs[key] = value attrs = self._adaptAttrs(attrs) return attrs def checkModify(self, attrs): """ Checks whether the attribute change can succeed before changing the attributes. If checks whether the attributes are listen in CAP_ATTRS and if the current object state is listed in CAP_ATTRS[NAME]. @param attrs: Attributes to change @type attrs: dict """ self.checkRole(Role.manager) mcon = self.mainConnection() direct = [] if self.DIRECT_ATTRS: if mcon: direct = mcon.getAllowedAttributes().keys() else: caps = getConnectionCapabilities(self.remoteType()) direct = caps["attrs"].keys() if caps else [] for key in attrs.keys(): if key in direct and not key in self.DIRECT_ATTRS_EXCLUDE: continue if key.startswith("_"): continue UserError.check(key in self.CUSTOM_ATTRS, code=UserError.UNSUPPORTED_ATTRIBUTE, message="Unsuported connection attribute: %s" % key, data={ "attribute": key, "id": self.id }) self.CUSTOM_ATTRS[key].check(self, attrs[key]) def modify(self, attrs): """ Sets the given attributes to their given values. This method first checks if the change can be made using checkModify() and then executes the attribute changes by calling modify_KEY(VALUE) for each key/value pair in attrs. After calling all these modify_KEY methods, it will save the object. Classes inheriting from this class should only implement the modify_KEY methods and not touch this method. @param attrs: Attributes to change @type attrs: dict """ with getLock(self): return self._modify(attrs) def _modify(self, attrs): self.checkModify(attrs) logging.logMessage("modify", category="connection", id=self.id, attrs=attrs) try: directAttrs = {} for key, value in attrs.iteritems(): if key in self.CUSTOM_ATTRS: getattr(self, "modify_%s" % key)(value) else: if key.startswith("_"): self.setAttribute(key, value) else: directAttrs[key] = value if directAttrs: self.setAttributes(directAttrs) mcon = self.mainConnection() if mcon: mcon.modify(self._remoteAttrs()) except Exception, exc: self.onError(exc) raise self.save() logging.logMessage("info", category="connection", id=self.id, info=self.info())