diff options
Diffstat (limited to 'vicn/core/state.py')
-rw-r--r-- | vicn/core/state.py | 177 |
1 files changed, 177 insertions, 0 deletions
diff --git a/vicn/core/state.py b/vicn/core/state.py new file mode 100644 index 00000000..d5069b24 --- /dev/null +++ b/vicn/core/state.py @@ -0,0 +1,177 @@ +#!/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 asyncio +import random +import string + +class NEVER_SET: + pass + +# Separator for components of the UUID +UUID_SEP = '-' + +# Length of the random component of the UUID +UUID_LEN = 5 + +class ResourceState: + UNINITIALIZED = 'UNINITIALIZED' + PENDING_DEPS = 'PENDING_DEPS' + DEPS_OK = 'DEPS_OK' + PENDING_INIT = 'PENDING_INIT' + INITIALIZED = 'INITIALIZED' + PENDING_GET = 'PENDING_GET' + GET_DONE = 'GET_DONE' + PENDING_KEYS = 'PENDING_KEYS' + KEYS_OK = 'KEYS_OK' + PENDING_CREATE = 'PENDING_CREATE' + CREATED = 'CREATED' + DIRTY = 'DIRTY' + CLEAN = 'CLEAN' + PENDING_UPDATE = 'PENDING_UPDATE' + PENDING_DELETE = 'PENDING_DELETE' + DELETED = 'DELETED' + +class AttributeState: + UNINITIALIZED = 'UNINITIALIZED' + INITIALIZED = 'INITIALIZED' + DIRTY = 'DIRTY' + PENDING_INIT = 'PENDING_INIT' + PENDING_UPDATE = 'PENDING_UPDATE' + CLEAN = 'CLEAN' + +class Operations: + SET = 'set' + LIST_ADD = 'add' + LIST_REMOVE = 'remove' + LIST_CLEAR = 'clear' + +class UUID: + def __init__(self, name, cls): + self._uuid = self._make_uuid(name, cls) + + def _make_uuid(self, name, cls): + """Generate a unique resource identifier + + The UUID consists in the type of the resource, to which is added a + random identifier of length UUID_LEN. Components of the UUID are + separated by UUID_SEP. + """ + uuid = ''.join(random.choice(string.ascii_uppercase + string.digits) + for _ in range(UUID_LEN)) + if name: + uuid = name # + UUID_SEP + uuid + return UUID_SEP.join([cls.__name__, uuid]) + + def __repr__(self): + return '<UUID {}>'.format(self._uuid) + + def __lt__(self, other): + return self._uuid < other._uuid + + __str__ = __repr__ + +class PendingValue: + def __init__(self, value = None): + self.clear(value) + + def clear(self, value=NEVER_SET): + self.value = NEVER_SET + self.operations = list() + + def trigger(self, action, value, cur_value = None): + + if self.value is NEVER_SET: + if cur_value is not None: + self.value = cur_value + + if action == Operations.SET: + self.value = value + self.operations = [(Operations.SET, value)] + elif action == Operations.LIST_CLEAR: + self.value = list() + self.operations = [(Operations.LIST_CLEAR, None)] + else: + if action == Operations.LIST_ADD: + self.value.append(value) + elif action == Operations.LIST_REMOVE: + self.value.remove(value) + else: + raise RuntimeError + self.operations.append((action, value)) + +class InstanceState: + def __init__(self, manager, instance, name = None): + + # Unique identifier for the instance. This is useful for relation + # between resources + self.uuid = UUID(name, instance.__class__) + self.instance = instance + + # Instance manager + self.manager = manager + + # Events + self.events = dict() + + # Stores the requested value : attribute_name -> requested operations = + # LIST set add remove clear + self.dirty = dict() + + + # Initialize resource state + self.lock = asyncio.Lock() + self.write_lock = asyncio.Lock() + self.state = ResourceState.UNINITIALIZED + self.clean = asyncio.Event() + self.clean.clear() + self.init = asyncio.Event() + self.init.clear() + self.change_event = asyncio.Event() + self.change_event.clear() + self.change_success = None + self.change_value = None + self.log = list() + + self.attr_lock = dict() + self.attr_init = dict() + self.attr_clean = dict() + self.attr_state = dict() + self.attr_change_event = dict() + self.attr_change_success = dict() + self.attr_change_value= dict() + self.attr_log = dict() + # Initialize attribute state + for attribute in instance.iter_attributes(): + self.attr_lock[attribute.name] = asyncio.Lock() + self.attr_init[attribute.name] = asyncio.Event() + self.attr_clean[attribute.name] = asyncio.Event() + self.attr_state[attribute.name] = AttributeState.UNINITIALIZED + self.attr_change_event[attribute.name] = asyncio.Event() + self.attr_change_event[attribute.name].clear() + self.attr_change_success[attribute.name] = None + self.attr_change_value[attribute.name] = None + self.dirty[attribute.name] = PendingValue(NEVER_SET) + self.attr_log[attribute.name] = list() + + def set_dirty(self, attr_name): + self.attr_dirty.add(attr_name) + self.manager.set_dirty(self.uuid) + + def trigger(self, attribute_name, action, value): + self.dirty[attribute_name].trigger(action, value) |