aboutsummaryrefslogtreecommitdiffstats
path: root/vicn/core/requirement.py
diff options
context:
space:
mode:
Diffstat (limited to 'vicn/core/requirement.py')
-rw-r--r--vicn/core/requirement.py229
1 files changed, 229 insertions, 0 deletions
diff --git a/vicn/core/requirement.py b/vicn/core/requirement.py
new file mode 100644
index 00000000..c42cecd9
--- /dev/null
+++ b/vicn/core/requirement.py
@@ -0,0 +1,229 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2017 Cisco and/or its affiliates.
+#
+# 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.
+#
+
+import enum
+
+from netmodel.model.mapper import ObjectSpecification
+from vicn.core.exception import VICNException
+
+#------------------------------------------------------------------------------
+# Enums
+#------------------------------------------------------------------------------
+
+class RequirementScope(enum.Enum):
+ INSTANCE = 'Instance'
+ CLASS = 'Class'
+
+#------------------------------------------------------------------------------
+# Exceptions
+#------------------------------------------------------------------------------
+
+class RequirementError(VICNException):
+
+ def __init__(self, instance, attr):
+ super().__init__()
+ self._instance = instance
+ self._attr = attr
+
+ def __str__(self):
+ return "Requirement on {}.{} could not be satisfied:".format(
+ self._instance, self._attr)
+
+class RequiredAttributeError(RequirementError):
+
+ def __str__(self):
+ return super().__str__() + "could not find attribute {}".format(
+ self._attr)
+
+class RequiredPropertyError(RequirementError):
+ def __init__(self, instance, attr, prop):
+ super().__init__(instance, attr)
+ self._prop = prop
+
+ def __str__(self):
+ return super().__str__()+ "property {} is not verified".format(
+ self._prop)
+
+#------------------------------------------------------------------------------
+# Class: Property
+#------------------------------------------------------------------------------
+
+class Property:
+
+ TYPE_ANY_OF = 0
+ #XXX cant think of a good use case for that
+ #TYPE_ALL_OF = 1
+
+ def __init__(self, value, property_type=TYPE_ANY_OF):
+ self._type = property_type
+ try:
+ self._value = set(value)
+ except TypeError: #value is not iterable, it is a single value
+ self._value = set()
+ self._value.add(value)
+
+
+ @property
+ def property_type(self):
+ return self._type
+
+ @property
+ def value(self):
+ return self._value
+
+ def check(self, value):
+ return value in self._value
+
+ def __str__(self):
+ return str(self._value)
+
+ def merge(self, other):
+ assert self._type is other.property_type, \
+ "Properties must be of same type to be merged"
+
+ #if self._type is TYPE_ANY_OF:
+ self._value.intersection_update(other.value)
+ #elif self._type is TYPE_ALL_OF:
+ # self._value.union_update(other.value)
+
+#------------------------------------------------------------------------------
+# Class: Requirement
+#------------------------------------------------------------------------------
+
+class Requirement(ObjectSpecification):
+ """Resource requirement
+
+ This class allows to specify a requirement on a given resource, or on a
+ class of resources (all instances of a given class).
+ """
+
+ #--------------------------------------------------------------------------
+ # Constructor
+ #--------------------------------------------------------------------------
+
+ def __init__(self, requirement_type, properties = None,
+ capabilities = None, scope = RequirementScope.INSTANCE,
+ fatal = True, must_be_setup = False):
+ """
+ Args:
+ requirement_type (): the attribute on which the requirement is made
+ properties (Optional[XXX]): XXX (defaults to None)
+ scope (Optional[enum RequirementScope]): Is the requirement dealing
+ with an instance, or a class (all instance of the class)
+ (defaults to RequirementScope.INSTANCE)
+ fatal (Optional[bool]): is the failure of the requirement fatal
+ (raises an error), or not (raises a warning) (defaults to True)
+ must_be_setup (Optional[bool]): defaults to False
+ """
+ self._type = requirement_type
+ self._properties = {}
+ if properties:
+ for prop in properties:
+ self._properties[prop] = Property(properties[prop])
+ self._capabilities = capabilities if capabilities else set()
+ self._scope = scope
+ self._fatal = fatal
+ self._must_be_up = must_be_setup
+
+ #--------------------------------------------------------------------------
+ # Accessors and properties
+ #--------------------------------------------------------------------------
+
+ @property
+ def properties(self):
+ return self._properties
+
+ @property
+ def requirement_type(self):
+ return self._type
+
+ @property
+ def must_be_up(self):
+ return self._must_be_up
+
+ #--------------------------------------------------------------------------
+ # Display
+ #--------------------------------------------------------------------------
+
+ def __str__(self):
+ prop_str = "{" + ",".join(map(lambda x: "'{}': {}".format(x,
+ self._properties[x]), self._properties.keys())) +"}"
+ return "<type={}, properties={}, must_be_up={}>".format(self._type,
+ prop_str, self._must_be_up)
+
+ #--------------------------------------------------------------------------
+ # Requirement operators
+ #--------------------------------------------------------------------------
+
+ def check(self, instance):
+ if not hasattr(instance, self._type):
+ raise RequiredAttributeError(instance, self._type)
+
+ instance_attr = getattr(instance, self._type)
+ if not instance_attr:
+ raise TypeError("instance_attr is none")
+
+ for prop in self.properties:
+ if not hasattr(instance_attr, prop):
+ raise RequiredAttributeError(instance, self._type)
+ if not self._properties[prop].check(getattr(instance_attr, prop)):
+ raise RequiredPropertyError(instance, self._type, prop)
+
+ return True
+
+ #--------------------------------------------------------------------------
+ # Requirement logic
+ #--------------------------------------------------------------------------
+
+ def merge(self, other):
+ assert other.requirement_type == self._type, \
+ "Cannot merge Requirements with different types"
+
+ for prop in other.properties:
+ if prop in self._properties:
+ self._properties[prop].merge(other.properties[prop])
+ else:
+ self._properties[prop] = other.properties[prop]
+
+ if other._capabilities:
+ self._capabilities |= other._capabilities
+
+#------------------------------------------------------------------------------
+# Class: Requirement list
+#------------------------------------------------------------------------------
+
+class RequirementList(list):
+
+ def __init__(self,x=None):
+ super().__init__()
+ if x:
+ self.extend(x)
+
+ def append(self,x):
+ assert isinstance(x,Requirement)
+ # XXX O(n) right now, might be able to do better
+ for req in self:
+ if req.requirement_type == x.requirement_type:
+ req.merge(x)
+ return
+
+ super().append(x)
+
+ def extend(self, x):
+ for y in x:
+ self.append(y)