Transitioned from file state to DB state
Managing states are now made with a DB. Fixes on the file state manager. Change-Id: I89bef295bfec965c06ce7df75fc6fb5cf87f6f57
This commit is contained in:
parent
5765daf6c2
commit
6903c5c2fd
|
@ -0,0 +1,38 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2014 Objectif Libre
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
# @author: Stéphane Albert
|
||||
#
|
||||
from oslo.config import cfg
|
||||
from oslo.db.sqlalchemy import session
|
||||
|
||||
_FACADE = None
|
||||
|
||||
|
||||
def _create_facade_lazily():
|
||||
global _FACADE
|
||||
if _FACADE is None:
|
||||
_FACADE = session.EngineFacade.from_config(cfg.CONF)
|
||||
return _FACADE
|
||||
|
||||
|
||||
def get_engine():
|
||||
facade = _create_facade_lazily()
|
||||
return facade.get_engine()
|
||||
|
||||
|
||||
def get_session(**kwargs):
|
||||
facade = _create_facade_lazily()
|
||||
return facade.get_session(**kwargs)
|
|
@ -0,0 +1,70 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2014 Objectif Libre
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
# @author: Stéphane Albert
|
||||
#
|
||||
import abc
|
||||
|
||||
from oslo.config import cfg
|
||||
from oslo.db import api as db_api
|
||||
import six
|
||||
|
||||
_BACKEND_MAPPING = {'sqlalchemy': 'cloudkitty.db.sqlalchemy.api'}
|
||||
IMPL = db_api.DBAPI.from_config(cfg.CONF,
|
||||
backend_mapping=_BACKEND_MAPPING,
|
||||
lazy=True)
|
||||
|
||||
|
||||
def get_instance():
|
||||
"""Return a DB API instance."""
|
||||
return IMPL
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
class State(object):
|
||||
"""Base class for state tracking."""
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_state(self, name):
|
||||
"""Retrieve the current state.
|
||||
|
||||
:param name: Name of the state
|
||||
:return float: State value
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def set_state(self, name, state):
|
||||
"""Store the state.
|
||||
|
||||
:param name: Name of the state
|
||||
:param state: State value
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def get_metadata(self, name):
|
||||
"""Retrieve state metadata
|
||||
|
||||
:param name: Name of the state
|
||||
:return str: Return a json dict with all metadata attached to this
|
||||
state.
|
||||
"""
|
||||
|
||||
@abc.abstractmethod
|
||||
def set_metadata(self, name, metadata):
|
||||
"""Store the state metadata.
|
||||
|
||||
:param name: Name of the state
|
||||
:param metadata: Metadata value
|
||||
"""
|
|
@ -0,0 +1,98 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2014 Objectif Libre
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
# @author: Stéphane Albert
|
||||
#
|
||||
from oslo.config import cfg
|
||||
from oslo.db.sqlalchemy import utils
|
||||
import sqlalchemy
|
||||
|
||||
from cloudkitty import config # NOQA
|
||||
from cloudkitty import db
|
||||
from cloudkitty.db import api
|
||||
from cloudkitty.db.sqlalchemy import models
|
||||
from cloudkitty.openstack.common import log as logging
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def get_backend():
|
||||
return DBAPIManager
|
||||
|
||||
|
||||
class State(api.State):
|
||||
|
||||
def get_state(self, name):
|
||||
session = db.get_session()
|
||||
try:
|
||||
return bool(utils.model_query(
|
||||
models.StateInfo,
|
||||
session
|
||||
).filter_by(
|
||||
name=name,
|
||||
).value('state'))
|
||||
except sqlalchemy.orm.exc.NoResultFound:
|
||||
return None
|
||||
|
||||
def set_state(self, name, state):
|
||||
session = db.get_session()
|
||||
with session.begin():
|
||||
try:
|
||||
q = utils.model_query(
|
||||
models.StateInfo,
|
||||
session
|
||||
).filter_by(
|
||||
name=name,
|
||||
).with_lockmode('update')
|
||||
db_state = q.one()
|
||||
db_state.state = state
|
||||
except sqlalchemy.orm.exc.NoResultFound:
|
||||
db_state = models.StateInfo(name=name, state=state)
|
||||
session.add(db_state)
|
||||
return bool(db_state.state)
|
||||
|
||||
def get_metadata(self, name):
|
||||
session = db.get_session()
|
||||
return utils.model_query(
|
||||
models.StateInfo,
|
||||
session
|
||||
).filter_by(
|
||||
name=name,
|
||||
).value('s_metadata')
|
||||
|
||||
def set_metadata(self, name, metadata):
|
||||
session = db.get_session()
|
||||
try:
|
||||
db_state = utils.model_query(
|
||||
models.StateInfo,
|
||||
session
|
||||
).filter_by(
|
||||
name=name,
|
||||
).with_lockmode('update').one()
|
||||
db_state.s_metadata = metadata
|
||||
except sqlalchemy.orm.exc.NoResultFound:
|
||||
db_state = models.StateInfo(name=name, s_metadata=metadata)
|
||||
session.add(db_state)
|
||||
finally:
|
||||
session.flush()
|
||||
|
||||
|
||||
class DBAPIManager(object):
|
||||
|
||||
@staticmethod
|
||||
def get_state():
|
||||
return State()
|
|
@ -0,0 +1,47 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2014 Objectif Libre
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
# @author: Stéphane Albert
|
||||
#
|
||||
from oslo.db.sqlalchemy import models
|
||||
import sqlalchemy
|
||||
from sqlalchemy.ext import declarative
|
||||
|
||||
|
||||
Base = declarative.declarative_base()
|
||||
|
||||
|
||||
class StateInfo(Base, models.ModelBase):
|
||||
"""State
|
||||
|
||||
"""
|
||||
|
||||
__tablename__ = 'states'
|
||||
|
||||
name = sqlalchemy.Column(sqlalchemy.String(255),
|
||||
primary_key=True)
|
||||
state = sqlalchemy.Column(
|
||||
sqlalchemy.Float(),
|
||||
nullable=False)
|
||||
s_metadata = sqlalchemy.Column(sqlalchemy.Text(),
|
||||
nullable=True,
|
||||
default='')
|
||||
|
||||
def __repr__(self):
|
||||
return ('<StateInfo[{name}]: '
|
||||
'state={state} metadata={metadata}>').format(
|
||||
name=self.name,
|
||||
state=self.state,
|
||||
metadata=self.s_metadata)
|
|
@ -49,10 +49,8 @@ class Orchestrator(object):
|
|||
auth_url=CONF.auth.url)
|
||||
|
||||
s_backend = i_utils.import_class(CONF.state.backend)
|
||||
self.sm = state.StateManager(s_backend,
|
||||
CONF.state.basepath,
|
||||
self.keystone.user_id,
|
||||
'osrtf')
|
||||
self.sm = state.DBStateManager(self.keystone.user_id,
|
||||
'osrtf')
|
||||
|
||||
collector_args = {'user': CONF.auth.username,
|
||||
'password': CONF.auth.password,
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
#
|
||||
import json
|
||||
|
||||
from cloudkitty.db import api
|
||||
|
||||
|
||||
class StateManager(object):
|
||||
def __init__(self, state_backend, state_basepath, user_id, report_type,
|
||||
|
@ -31,8 +33,13 @@ class StateManager(object):
|
|||
self._ts = None
|
||||
self._metadata = {}
|
||||
|
||||
# Load states
|
||||
self._load()
|
||||
|
||||
def _gen_filename(self):
|
||||
filename = '{}_{}.state'.format(self._type, self._uid)
|
||||
# FIXME(sheeprine): Basepath can't be enforced at the moment
|
||||
filename = '{}_{}.state'.format(self._type,
|
||||
self._uid)
|
||||
return filename
|
||||
|
||||
def _open(self, mode='rb'):
|
||||
|
@ -43,9 +50,11 @@ class StateManager(object):
|
|||
def _load(self):
|
||||
try:
|
||||
state_file = self._open()
|
||||
state_data = json.loads(state_file.read())
|
||||
self._ts = state_data['timestamp']
|
||||
self._metadata = state_data['metadata']
|
||||
raw_data = state_file.read()
|
||||
if raw_data:
|
||||
state_data = json.loads(raw_data)
|
||||
self._ts = state_data['timestamp']
|
||||
self._metadata = state_data['metadata']
|
||||
state_file.close()
|
||||
except IOError:
|
||||
pass
|
||||
|
@ -82,3 +91,35 @@ class StateManager(object):
|
|||
if self._distributed:
|
||||
self._load()
|
||||
return self._metadata
|
||||
|
||||
|
||||
class DBStateManager(object):
|
||||
def __init__(self, user_id, report_type, distributed=False):
|
||||
self._state_name = self._gen_name(report_type, user_id)
|
||||
self._distributed = distributed
|
||||
self._db = api.get_instance().get_state()
|
||||
|
||||
def _gen_name(self, state_type, uid):
|
||||
name = '{}_{}'.format(state_type, uid)
|
||||
return name
|
||||
|
||||
def get_state(self):
|
||||
"""Get the state timestamp."""
|
||||
|
||||
return self._db.get_state(self._state_name)
|
||||
|
||||
def set_state(self, timestamp):
|
||||
"""Set the current state's timestamp."""
|
||||
|
||||
self._db.set_state(self._state_name, timestamp)
|
||||
|
||||
def get_metadata(self):
|
||||
"""Get metadata attached to the state."""
|
||||
|
||||
return json.loads(self._db.get_metadata(self._state_name))
|
||||
|
||||
def set_metadata(self, metadata):
|
||||
"""Set metadata attached to the state."""
|
||||
|
||||
self._db.set_metadata(self._state_name,
|
||||
json.dumps(metadata))
|
||||
|
|
|
@ -32,10 +32,8 @@ class BaseReportWriter(object):
|
|||
self._write_orchestrator = write_orchestrator
|
||||
self._write_backend = backend
|
||||
self._uid = user_id
|
||||
self._sm = state.StateManager(state_backend,
|
||||
None,
|
||||
self._uid,
|
||||
self.report_type)
|
||||
self._sm = state.DBStateManager(self._uid,
|
||||
self.report_type)
|
||||
self._report = None
|
||||
self._period = 3600
|
||||
|
||||
|
|
Loading…
Reference in New Issue