Source code for cobra.mit.naming

# Copyright 2019 Cisco Systems, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""The naming module for the ACI Python SDK (cobra)."""

import sys
if sys.version_info[0] == 3:
    from builtins import str
from builtins import object

from builtins import next
#from past.builtins import cmp
from cobra.mit.meta import ClassLoader
from collections import deque


[docs]class Rn(object): """ The Rn class is the relative name (Rn) of the managed object (MO). You can use Rn to convert between Rn of an MO its constituent naming values. The string form of Rn is {prefix}{val1}{prefix2}{Val2} (...) Note: The naming value is enclosed in brackets ([]) if the meta object specifies that properties be delimited. """
[docs] @classmethod def fromString(cls, classMeta, rnStr): """ Create a relative name object from the string form given the class meta :param classMeta: class meta of the mo class :type classMeta: cobra.mit.meta.ClassMeta :param rnStr: string form of rn :type rnStr: str :returns: Rn object :rtype: cobra.mit.naming.Rn """ def findBalancedPropDelims(rn, start): stk = deque() first = True end = start # loop through the rnStr looking for [ and append them to the deque # and ] and pop them off the deque, once things are balanced return # that character as the end of the Rn. while end < len(rn): if not first and len(stk) == 0: return end symbol = rn[end] if symbol == "[": if first and len(stk) == 0: first = False stk.append(symbol) elif symbol == "]": if first and len(stk) == 0: raise ValueError("Invalid Rn: Found closing prop " + "delimiter before opening prop " + "delimiter.") else: stk.pop() end += 1 return -1 def parseNamingProps(meta, rn): rnFormat = meta.rnFormat if not len(meta.namingProps): if rn == rnFormat: return [] else: raise ValueError('rn prefix mismatch') nPropVals = [] rnLen = len(rn) end = 0 start = 0 propMetaIter = iter(meta.namingProps) needPropDelimiter = False nPropMeta = None for rnPrefix, hasProp in meta.rnPrefixes: if start > end: # Parse the naming prop value if needPropDelimiter: end = findBalancedPropDelims(rn, start) else: end = rnStr.find(rnPrefix, start) if end == -1: raise ValueError("rn prefix '%s' not found in '%s'" % (rnPrefix, rn)) nPropVal = rn[start:end] if needPropDelimiter: nPropVal = nPropVal[1:-1] if nPropMeta: nPropVals.append(nPropVal) start = end # Find the rn prefix if not rn.startswith(rnPrefix, start): raise ValueError('rn "%s" must be %s' % (rn, rnFormat)) if hasProp: nPropMeta = next(propMetaIter) needPropDelimiter = nPropMeta.needDelimiter start += len(rnPrefix) end = rnLen nPropVal = rn[start:end] if needPropDelimiter: nPropVal = nPropVal[1:-1] if nPropMeta: nPropVals.append(nPropVal) return nPropVals namingVals = parseNamingProps(classMeta, rnStr) return Rn(classMeta, *namingVals)
[docs] def __init__(self, classMeta, *namingVals): """ Relative Name (Rn) of the Mo from class meta and list of naming values :param classMeta: class meta of the mo class :type classMeta: cobra.mit.meta.ClassMeta :param namingVals: list of naming values :type namingVals: list """ self.__namingVals = namingVals self.__meta = classMeta self.__rnStr = None
@property def namingVals(self): """ Iterator of naming values for this rn :returns: iterator of the naming values for this rn :rtype: iterator """ return iter(self.__namingVals) @property def namingValueList(self): return list(self.__namingVals) @property def meta(self): """ class meta of the mo class for this Rn :returns: class meta of the mo for this Rn :rtype: cobra.mit.meta.ClassMeta """ return self.__meta @property def moClass(self): """ Mo class for this Rn :returns: Mo class for this Rn :rtype: cobra.mit.mo.Mo """ return self.__meta.getClass() # def __cmp__(self, other): # """ # compares two rn objects using the natural ordering of their naming # values # :param other: other rn being compared # :type other: cobra.mit.naming.Rn # :returns: 0 if equal, > 0 if self is greater and < 0 if self is smaller # :rtype: int # """ # selfRnStr = str(self) # otherRnStr = str(other) # return cmp(selfRnStr, otherRnStr)
[docs] def __lt__(self, other): """Implement <.""" return str(self) < str(other)
[docs] def __le__(self, other): """Implement <=.""" return str(self) <= str(other)
[docs] def __eq__(self, other): """Implement ==.""" return str(self) == str(other)
[docs] def __ne__(self, other): """Implement !=.""" return str(self) != str(other)
[docs] def __gt__(self, other): """Implement >.""" return str(self) > str(other)
[docs] def __ge__(self, other): """Implement >=.""" return str(self) >= str(other)
def __str__(self): """ Returns the string form of the Rn :returns: string form of the Rn :rtype: str """ if not self.__rnStr: self.__rnStr = self.__makeRnStr() return self.__rnStr def __hash__(self): """ Returns the hash code for the Rn :returns: hash code for the Rn :rtype: int """ return hash(str(self)) def __makeRnStr(self): if self.__meta.namingProps: namingProps = {} namingValsIter = iter(self.__namingVals) for propMeta in self.__meta.namingProps: namingProps[propMeta.name] = next(namingValsIter) return self.__meta.rnFormat % namingProps else: return self.__meta.rnFormat
[docs]class Dn(object): """ The distinguished name (Dn) uniquely identifies a managed object (MO). A Dn is an ordered list of relative names, such as: dn = rn1/rn2/rn3/.... In this example, the Dn provides a fully qualified path for **user-john** from the top of the Mit to the Mo. dn = "uni/userext/user-john" """
[docs] @classmethod def fromString(cls, dnStr): """ Create a Dn from the string form of Dn. This method parses the dn string into its constituent Rn strings and creates the Rn objects. :param dnStr: string form of Dn :type dnStr: str :returns: Dn object :rtype: cobra.mit.naming.Dn """ rnStrs = cls.__splitDnStr(dnStr) newDn = Dn() pMeta = newDn.meta for rnStr in rnStrs: rnMeta = cls.__findChild(pMeta, rnStr) if rnMeta is None: raise ValueError("Dn '%s' cannot contain '%s'" % (str(newDn), rnStr)) rn = Rn.fromString(rnMeta, rnStr) newDn.appendRn(rn) pMeta = rnMeta return newDn
[docs] @classmethod def findCommonParent(cls, dns): """ Find the common parent for the given set of dn objects. :param dns: list of Dn objects :type dns: list :returns: Dn object of the common parent if any, else Dn for topRoot :rtype: cobra.mit.naming.Dn """ def allRnsEqual(allDns, i): firstRn = None for eachDn in allDns: currentRn = eachDn.__rns[i] if firstRn is None: firstRn = currentRn else: if firstRn != currentRn: return False # All Rns at this level are equal return True if not dns: return None elif len(dns) == 1: return dns[0] index = 0 maxLen = min([len(dn.__rns) for dn in dns]) while index < maxLen: if allRnsEqual(dns, index): index += 1 else: break if index == 0: return Dn() rns = dns[0].__rns return Dn(rns[:index])
[docs] def __init__(self, rns=None): """ Create a Dn from list of Rn objects. :param rns: list of Rns :type rns: list """ self.__dnStr = None self.__hash = None self.__rns = [] self.__class = ClassLoader.loadClass('cobra.model.top.Root') self.__meta = self.__class.meta if rns is None: rns = [] for rn in rns: self.appendRn(rn)
[docs] def rn(self, index=None): """ Returns the Rn object at the specified index. If index is None, then the Rn of the target Mo is returned :param index: index of the Rn object, this must be betwee 0 and the length of the Dn :type index: int :returns: Rn object at the specified index :rtype: cobra.mit.naming.Rn """ if index is None: return self.__rns[-1] return self.__rns[index]
[docs] def getAncestor(self, level): """ Returns the ancestor Dn based on the number of levels :param level: number of levels :type level: int :returns: Dn object of the ancestor as specified by the level param :rtype: cobra.mit.naming.Dn """ rns = self.__rns[:-level] return Dn(rns)
[docs] def getParent(self): """ Returns the parent Dn, same as:: self.getAncetor(1) :returns: Dn object of the immediate parent :rtype: cobra.mit.naming.Dn """ return self.getAncestor(1)
@property def isRoot(self): return len(self.__rns) == 0 @property def rns(self): """ Iterator for all the rns from topRoot to the target Mo :returns: iterator of Rns in this Dn :rtype: iterator """ return iter(self.__rns) @property def meta(self): """ class meta of the mo class for this Dn :returns: class meta of the mo for this Dn :rtype: cobra.mit.meta.ClassMeta """ return self.__meta @property def moClass(self): """ Mo class for this Dn :returns: Mo class for this Dn :rtype: cobra.mit.mo.Mo """ return self.__class @property def contextRoot(self): for rn in reversed(self.__rns): if rn.meta.isContextRoot: return rn.meta return None
[docs] def clone(self): """ Return a new copy of this Dn :returns: copy of this Dn :rtype: cobra.mit.naming.Dn """ newDn = Dn() for rn in self.__rns: newDn.appendRn(rn) return newDn
[docs] def appendRn(self, rn): """ Appends an Rn to this Dn, changes the target Mo """ if len(self.__rns) == 0 and str(rn) == '': # ignore addition of topRoot to topRoot its just a clone side-effect return rnClassName = rn.meta.className if rnClassName not in self.__meta.childClasses and not rn.meta.isWireOnly: className = str(self.meta.className) raise ValueError("'%s' cannot contain '%s'" % (className, str(rnClassName))) self.__rns.append(rn) self.__meta = rn.meta self.__class = rn.moClass self.__dnStr = None self.__hash = None
[docs] def isDescendantOf(self, ancestorDn): """ Return True if this Dn is a descendant of the other Dn :param ancestorDn: Dn being compared for ancestory :type ancestorDn: cobra.mit.naming.Dn :returns: True if this Dn is a descendant of the other Dn else False :rtype: boolean """ ansDnStr = str(ancestorDn) dnStr = str(self) return (dnStr != ansDnStr and len(self) > len(ancestorDn) and dnStr.startswith(ansDnStr))
[docs] def isAncestorOf(self, descendantDn): """ Return True if this Dn is an ancestor of the other Dn :param descendantDn: Dn being compared for descendants :type descendantDn: cobra.mit.naming.Dn :returns: True if this Dn is an ancestor of the other Dn else False :rtype: boolean """ return descendantDn.isDescendantOf(self)
def __len__(self): """ Returns the number of Rns in this Dn :returns: number of rns in the dn :rtype: int """ return len(self.__rns) def __str__(self): """ Returns the string form of the Dn :returns: string form of the Dn :rtype: str """ if not self.__dnStr: self.__dnStr = self.__makeDn() return self.__dnStr # def __cmp__(self, other): # """ # compares two Dn objects using the natural ordering of their Rns # :param other: other Dn being compared # :type other: cobra.mit.naming.Dn # :returns: 0 if equal, > 0 if self is greater and < 0 if self is smaller # :rtype: int # """ # return cmp(str(self), str(other))
[docs] def __lt__(self, other): """Implement <.""" return str(self) < str(other)
[docs] def __le__(self, other): """Implement <=.""" return str(self) <= str(other)
[docs] def __eq__(self, other): """Implement ==.""" return str(self) == str(other)
[docs] def __ne__(self, other): """Implement !=.""" return str(self) != str(other)
[docs] def __gt__(self, other): """Implement >.""" return str(self) > str(other)
[docs] def __ge__(self, other): """Implement >=.""" return str(self) >= str(other)
def __hash__(self): """ Returns the hash code for the Dn :returns: hash code for the Dn :rtype: int """ if self.__hash is None: self.__hash = hash(str(self)) return self.__hash def __makeDn(self): rnStrs = [] for rn in self.__rns: rnStrs.append(str(rn)) return '/'.join(rnStrs) @classmethod def __splitDnStr(cls, dnStr): rnStrs = [] rnStr = "" delimCount = 0 for dnChar in dnStr: if delimCount == 0 and dnChar == '/': # Found rn string, eat the char and capture the Rn if rnStr: rnStrs.append(rnStr) rnStr = "" elif dnChar == '[': delimCount += 1 rnStr += dnChar elif dnChar == ']': delimCount -= 1 rnStr += dnChar else: rnStr += dnChar if rnStr: rnStrs.append(rnStr) if delimCount != 0: raise ValueError("Invalid dn '%s' with unbalanced delimiters" % dnStr) return rnStrs @classmethod def __findChild(cls, pMeta, rnStr): # This method assumes that the childNamesAndRnPrefix in the meta # is sorted with longest prefix first. This will allow us to match # child prefixes that are sub strings. For example 'ac' and 'action' # where the list will have 'action' first and then 'ac'. This way # it is guaranteed that the longest prefix is matched first for childClassName, childRnPrefix in pMeta.childNamesAndRnPrefix: if rnStr.startswith(childRnPrefix): childClass = pMeta.childClasses[childClassName] return childClass.meta return None