group-based-policy/gbpservice/nfp/core/path.py

170 lines
4.5 KiB
Python

# 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.
from gbpservice.nfp.core import log as nfp_logging
import collections
deque = collections.deque
LOG = nfp_logging.getLogger(__name__)
class Supress(object):
def __init__(self, ignore_list=None):
self._ignore = ignore_list or []
def __enter__(self):
pass
def __exit__(self, e_type, e_value, traceback):
if e_type in self._ignore:
return True
for exception in self._ignore:
if isinstance(e_type, exception):
return True
class Path(object):
def __init__(self, name):
self._waitq = deque()
self.name = name
self.count = 0
self.invalid = False
def queue(self, event):
self._waitq.append(event)
def pop(self):
events = []
with Supress([IndexError]):
events.append(self._waitq.popleft())
return events
def done(self):
self._waitq.clear()
# {'key': {'current':Path, 'waiting':Path}
paths = {}
def run():
for key, path in paths.iteritems():
if path['current'].count == 0:
path['current'].done()
if path['waiting'].name != 'INVALID':
path['current'] = path['waiting']
path['current'].invalid = False
path['waiting'] = Path('INVALID')
events = []
# Get any queued events in the current path
for key, path in paths.iteritems():
events += path['current'].pop()
return events
def event_complete(event):
name = event.desc.path_type
key = event.desc.path_key
if not name:
return
name = name.upper()
with Supress([KeyError]):
path = paths[key]
if path['current'].name != name:
return
path['current'].count -= 1
def schedule_event(event):
name = event.desc.path_type
key = event.desc.path_key
if not name:
return 'schedule'
name = name.upper()
try:
path = paths[key]
if path['current'].name == name:
if path['current'].invalid:
return 'discard'
path['current'].count += 1
return 'schedule'
if path['waiting'].name == name:
path['waiting'].queue(event)
return 'wait'
if path['current'].name != name:
return 'discard'
except Exception:
return 'schedule'
return 'schedule'
def path_complete(path_type, key):
try:
path = paths[key]
if path['current'].name == path_type.upper() and (
path['waiting'].name == 'INVALID'):
paths.pop(key)
except KeyError:
message = "Path completion - %s path does not exist with key %s" % (
path_type, key)
LOG.debug(message)
def create_path(key):
# Create cannot progress if there is already a path
# with the same key in any state
try:
path = paths[key]
assert False, "Path (%s) with key (%s) is already in progress" % (
path['current'].name, key)
except KeyError:
# Create new path
paths[key] = {'current': Path('CREATE'), 'waiting': Path('INVALID')}
def delete_path(key):
try:
path = paths[key]
if path['current'].name != 'DELETE':
path['waiting'] = Path('DELETE')
path['current'].invalid = True
else:
assert False, ("Delete Path (%s) with key (%s)"
"is already in progress" % (
path['current'].name, key))
except KeyError:
paths[key] = {'current': Path('DELETE'), 'waiting': Path('INVALID')}
def update_path(key):
# Update cannot progress if there is DELETE already in progress
# or DELETE already waiting.
try:
path = paths[key]
assert False, "Path (%s) with key (%s) is in progress" % (
path.name, key)
except KeyError:
# Create new path
paths[key] = {'current': Path('UPDATE'), 'waiting': Path('INVALID')}