Fix pylinters
Change-Id: I3b17d87f821cadab39b9bf4e82043e79f20456ef
This commit is contained in:
parent
26719e3c48
commit
ebbb899953
@ -19,15 +19,24 @@
|
||||
parent: vexxhost-promote-docker-image
|
||||
vars: *atmosphere_images
|
||||
|
||||
- job:
|
||||
name: atmosphere:linters:tox
|
||||
parent: tox-linters
|
||||
vars:
|
||||
python_version: 3.7
|
||||
|
||||
- project:
|
||||
check:
|
||||
jobs:
|
||||
- atmosphere:linters:tox
|
||||
- tox-py37
|
||||
- atmosphere:image:build
|
||||
gate:
|
||||
jobs:
|
||||
- atmosphere:linters:tox
|
||||
- tox-py37
|
||||
- atmosphere:image:upload
|
||||
promote:
|
||||
jobs:
|
||||
- atmosphere:linters:tox
|
||||
- atmosphere:image:promote
|
||||
|
@ -12,11 +12,14 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Ingress
|
||||
|
||||
"""
|
||||
|
||||
from flask import Blueprint
|
||||
from flask import request
|
||||
from flask import abort
|
||||
from flask import jsonify
|
||||
from dateutil.relativedelta import relativedelta
|
||||
|
||||
from atmosphere.app import create_app
|
||||
from atmosphere import exceptions
|
||||
@ -27,6 +30,7 @@ blueprint = Blueprint('ingress', __name__)
|
||||
|
||||
|
||||
def init_application(config=None):
|
||||
"""init_application"""
|
||||
app = create_app(config)
|
||||
app.register_blueprint(blueprint)
|
||||
return app
|
||||
@ -34,20 +38,17 @@ def init_application(config=None):
|
||||
|
||||
@blueprint.route('/v1/event', methods=['POST'])
|
||||
def event():
|
||||
"""event"""
|
||||
if request.json is None:
|
||||
abort(400)
|
||||
|
||||
for event in request.json:
|
||||
print(jsonify(event).get_data(True))
|
||||
event = utils.normalize_event(event)
|
||||
for event_data in request.json:
|
||||
print(jsonify(event_data).get_data(True))
|
||||
event_data = utils.normalize_event(event_data)
|
||||
|
||||
try:
|
||||
resource = models.Resource.get_or_create(event)
|
||||
models.Resource.get_or_create(event_data)
|
||||
except (exceptions.EventTooOld, exceptions.IgnoredEvent):
|
||||
return '', 202
|
||||
|
||||
# TODO(mnaser): Drop this logging eventually...
|
||||
print(jsonify(event).get_data(True))
|
||||
print(jsonify(resource.serialize).get_data(True))
|
||||
|
||||
return '', 204
|
||||
|
@ -12,6 +12,9 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""App
|
||||
|
||||
"""
|
||||
import os
|
||||
|
||||
from flask import Flask
|
||||
@ -20,6 +23,7 @@ from atmosphere import models
|
||||
|
||||
|
||||
def create_app(config=None):
|
||||
"""create_app"""
|
||||
app = Flask(__name__)
|
||||
|
||||
if config is not None:
|
||||
@ -39,5 +43,3 @@ def create_app(config=None):
|
||||
models.migrate.init_app(app, models.db, directory=migrations_path)
|
||||
|
||||
return app
|
||||
|
||||
|
||||
|
@ -12,20 +12,27 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Exceptions
|
||||
|
||||
"""
|
||||
|
||||
from werkzeug import exceptions
|
||||
|
||||
|
||||
class UnsupportedEventType(exceptions.BadRequest):
|
||||
"""UnsupportedEventType"""
|
||||
description = 'Unsupported event type'
|
||||
|
||||
|
||||
class MultipleOpenPeriods(exceptions.Conflict):
|
||||
"""MultipleOpenPeriods"""
|
||||
description = 'Multiple open periods'
|
||||
|
||||
|
||||
class IgnoredEvent(Exception):
|
||||
"""IgnoredEvent"""
|
||||
description = 'Ignored event type'
|
||||
|
||||
|
||||
class EventTooOld(Exception):
|
||||
pass
|
||||
"""EventTooOld"""
|
||||
|
@ -12,18 +12,23 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Models
|
||||
|
||||
"""
|
||||
# pylint: disable=R0903
|
||||
# pylint: disable=W0223
|
||||
# pylint: disable=no-member
|
||||
# pylint: disable=not-an-iterable
|
||||
from datetime import datetime
|
||||
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
from flask_migrate import Migrate
|
||||
from sqlalchemy import func
|
||||
from sqlalchemy import exc
|
||||
from sqlalchemy.orm import exc as orm_exc
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from sqlalchemy.types import TypeDecorator
|
||||
|
||||
from atmosphere import exceptions
|
||||
from atmosphere import utils
|
||||
|
||||
db = SQLAlchemy()
|
||||
migrate = Migrate()
|
||||
@ -32,11 +37,44 @@ migrate = Migrate()
|
||||
MONTH_START = relativedelta(day=1, hour=0, minute=0, second=0, microsecond=0)
|
||||
|
||||
|
||||
def get_model_type_from_event(event):
|
||||
"""get_model_type_from_event"""
|
||||
if event.startswith('compute.instance'):
|
||||
return Instance, InstanceSpec
|
||||
if event.startswith('aggregate.'):
|
||||
raise exceptions.IgnoredEvent
|
||||
if event.startswith('compute_task.'):
|
||||
raise exceptions.IgnoredEvent
|
||||
if event.startswith('compute.'):
|
||||
raise exceptions.IgnoredEvent
|
||||
if event.startswith('flavor.'):
|
||||
raise exceptions.IgnoredEvent
|
||||
if event.startswith('keypair.'):
|
||||
raise exceptions.IgnoredEvent
|
||||
if event.startswith('libvirt.'):
|
||||
raise exceptions.IgnoredEvent
|
||||
if event.startswith('metrics.'):
|
||||
raise exceptions.IgnoredEvent
|
||||
if event.startswith('scheduler.'):
|
||||
raise exceptions.IgnoredEvent
|
||||
if event.startswith('server_group.'):
|
||||
raise exceptions.IgnoredEvent
|
||||
if event.startswith('service.'):
|
||||
raise exceptions.IgnoredEvent
|
||||
if event == 'volume.usage':
|
||||
raise exceptions.IgnoredEvent
|
||||
|
||||
raise exceptions.UnsupportedEventType
|
||||
|
||||
|
||||
class GetOrCreateMixin:
|
||||
"""GetOrCreateMixin"""
|
||||
|
||||
@classmethod
|
||||
def get_or_create(self, event):
|
||||
query = self.query_from_event(event)
|
||||
new_instance = self.from_event(event)
|
||||
def get_or_create(cls, event):
|
||||
"""get_or_create"""
|
||||
query = cls.query_from_event(event)
|
||||
new_instance = cls.from_event(event)
|
||||
|
||||
db_instance = query.first()
|
||||
if db_instance is None:
|
||||
@ -54,6 +92,8 @@ class GetOrCreateMixin:
|
||||
|
||||
|
||||
class Resource(db.Model, GetOrCreateMixin):
|
||||
"""Resource"""
|
||||
|
||||
uuid = db.Column(db.String(36), primary_key=True)
|
||||
type = db.Column(db.String(32), nullable=False)
|
||||
project = db.Column(db.String(32), nullable=False)
|
||||
@ -66,8 +106,9 @@ class Resource(db.Model, GetOrCreateMixin):
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def from_event(self, event):
|
||||
cls, _ = utils.get_model_type_from_event(event['event_type'])
|
||||
def from_event(cls, event):
|
||||
"""from_event"""
|
||||
cls, _ = get_model_type_from_event(event['event_type'])
|
||||
|
||||
return cls(
|
||||
uuid=event['traits']['resource_id'],
|
||||
@ -76,8 +117,9 @@ class Resource(db.Model, GetOrCreateMixin):
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def query_from_event(self, event):
|
||||
cls, _ = utils.get_model_type_from_event(event['event_type'])
|
||||
def query_from_event(cls, event):
|
||||
"""query_from_event"""
|
||||
cls, _ = get_model_type_from_event(event['event_type'])
|
||||
|
||||
return cls.query.filter_by(
|
||||
uuid=event['traits']['resource_id'],
|
||||
@ -85,8 +127,9 @@ class Resource(db.Model, GetOrCreateMixin):
|
||||
).with_for_update()
|
||||
|
||||
@classmethod
|
||||
def get_or_create(self, event):
|
||||
resource = super(Resource, self).get_or_create(event)
|
||||
def get_or_create(cls, event):
|
||||
"""get_or_create"""
|
||||
resource = super(Resource, cls).get_or_create(event)
|
||||
|
||||
# If the last update is newer than our last update, we assume that
|
||||
# another event has been processed that is newer (so we should ignore
|
||||
@ -137,6 +180,7 @@ class Resource(db.Model, GetOrCreateMixin):
|
||||
return resource
|
||||
|
||||
def get_open_period(self):
|
||||
"""get_open_period"""
|
||||
open_periods = list(filter(lambda p: p.ended_at is None, self.periods))
|
||||
if len(open_periods) > 1:
|
||||
raise exceptions.MultipleOpenPeriods
|
||||
@ -154,16 +198,19 @@ class Resource(db.Model, GetOrCreateMixin):
|
||||
'project': self.project,
|
||||
'updated_at': self.updated_at,
|
||||
'periods': [p.serialize for p in self.periods],
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class Instance(Resource):
|
||||
"""Instance"""
|
||||
|
||||
__mapper_args__ = {
|
||||
'polymorphic_identity': 'OS::Nova::Server'
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def is_event_ignored(self, event):
|
||||
def is_event_ignored(cls, event):
|
||||
"""is_event_ignored"""
|
||||
vm_state_is_deleted = (event['traits']['state'] == 'deleted')
|
||||
no_deleted_at = ('deleted_at' not in event['traits'])
|
||||
|
||||
@ -174,21 +221,27 @@ class Instance(Resource):
|
||||
|
||||
|
||||
class BigIntegerDateTime(TypeDecorator):
|
||||
"""BigIntegerDateTime"""
|
||||
|
||||
impl = db.BigInteger
|
||||
|
||||
def process_bind_param(self, value, _):
|
||||
"""process_bind_param"""
|
||||
if value is None:
|
||||
return None
|
||||
assert isinstance(value, datetime)
|
||||
return value.timestamp() * 1000
|
||||
|
||||
def process_result_value(self, value, _):
|
||||
"""process_result_value"""
|
||||
if value is None:
|
||||
return None
|
||||
return datetime.fromtimestamp(value / 1000)
|
||||
|
||||
|
||||
class Period(db.Model):
|
||||
"""Period"""
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
resource_uuid = db.Column(db.String(36), db.ForeignKey('resource.uuid'),
|
||||
nullable=False)
|
||||
@ -200,6 +253,7 @@ class Period(db.Model):
|
||||
|
||||
@property
|
||||
def seconds(self):
|
||||
"""seconds"""
|
||||
ended_at = self.ended_at
|
||||
if ended_at is None:
|
||||
ended_at = datetime.now()
|
||||
@ -214,10 +268,12 @@ class Period(db.Model):
|
||||
'ended_at': self.ended_at,
|
||||
'seconds': self.seconds,
|
||||
'spec': self.spec.serialize,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class Spec(db.Model, GetOrCreateMixin):
|
||||
"""Spec"""
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
type = db.Column(db.String(32))
|
||||
|
||||
@ -226,16 +282,18 @@ class Spec(db.Model, GetOrCreateMixin):
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def from_event(self, event):
|
||||
_, cls = utils.get_model_type_from_event(event['event_type'])
|
||||
def from_event(cls, event):
|
||||
"""from_event"""
|
||||
_, cls = get_model_type_from_event(event['event_type'])
|
||||
spec = {c.name: event['traits'][c.name]
|
||||
for c in cls.__table__.columns if c.name != 'id'}
|
||||
|
||||
return cls(**spec)
|
||||
|
||||
@classmethod
|
||||
def query_from_event(self, event):
|
||||
_, cls = utils.get_model_type_from_event(event['event_type'])
|
||||
def query_from_event(cls, event):
|
||||
"""query_from_event"""
|
||||
_, cls = get_model_type_from_event(event['event_type'])
|
||||
spec = {c.name: event['traits'][c.name]
|
||||
for c in cls.__table__.columns if c.name != 'id'}
|
||||
|
||||
@ -243,6 +301,8 @@ class Spec(db.Model, GetOrCreateMixin):
|
||||
|
||||
|
||||
class InstanceSpec(Spec):
|
||||
"""InstanceSpec"""
|
||||
|
||||
id = db.Column(db.Integer, db.ForeignKey('spec.id'), primary_key=True)
|
||||
instance_type = db.Column(db.String(255))
|
||||
state = db.Column(db.String(255))
|
||||
@ -252,7 +312,7 @@ class InstanceSpec(Spec):
|
||||
)
|
||||
|
||||
__mapper_args__ = {
|
||||
'polymorphic_identity': 'OS::Nova::Server',
|
||||
'polymorphic_identity': 'OS::Nova::Server',
|
||||
}
|
||||
|
||||
@property
|
||||
@ -262,4 +322,4 @@ class InstanceSpec(Spec):
|
||||
return {
|
||||
'instance_type': self.instance_type,
|
||||
'state': self.state,
|
||||
}
|
||||
}
|
||||
|
@ -44,18 +44,18 @@ class TestNormalizeEvent:
|
||||
|
||||
class TestModelTypeDetection:
|
||||
def test_compute_instance(self):
|
||||
assert utils.get_model_type_from_event('compute.instance.exists') == \
|
||||
assert models.get_model_type_from_event('compute.instance.exists') == \
|
||||
(models.Instance, models.InstanceSpec)
|
||||
|
||||
def test_ignored_resource(self, ignored_event):
|
||||
with pytest.raises(exceptions.IgnoredEvent) as e:
|
||||
utils.get_model_type_from_event(ignored_event)
|
||||
models.get_model_type_from_event(ignored_event)
|
||||
|
||||
assert e.value.description == "Ignored event type"
|
||||
|
||||
def test_unknown_resource(self):
|
||||
with pytest.raises(exceptions.UnsupportedEventType) as e:
|
||||
utils.get_model_type_from_event('foobar')
|
||||
models.get_model_type_from_event('foobar')
|
||||
|
||||
assert e.value.code == 400
|
||||
assert e.value.description == "Unsupported event type"
|
||||
|
@ -12,14 +12,16 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Utils
|
||||
|
||||
"""
|
||||
|
||||
from ceilometer.event import models as ceilometer_models
|
||||
from dateutil import parser
|
||||
|
||||
from atmosphere import exceptions
|
||||
from atmosphere import models
|
||||
|
||||
|
||||
def normalize_event(event):
|
||||
"""normalize_event"""
|
||||
event['generated'] = parser.parse(event['generated'])
|
||||
event['traits'] = {
|
||||
k: ceilometer_models.Trait.convert_value(t, v)
|
||||
@ -27,34 +29,3 @@ def normalize_event(event):
|
||||
}
|
||||
|
||||
return event
|
||||
|
||||
|
||||
def get_model_type_from_event(event):
|
||||
if event.startswith('compute.instance'):
|
||||
return models.Instance, models.InstanceSpec
|
||||
if event.startswith('aggregate.'):
|
||||
raise exceptions.IgnoredEvent
|
||||
if event.startswith('compute_task.'):
|
||||
raise exceptions.IgnoredEvent
|
||||
if event.startswith('compute.'):
|
||||
raise exceptions.IgnoredEvent
|
||||
if event.startswith('flavor.'):
|
||||
raise exceptions.IgnoredEvent
|
||||
if event.startswith('keypair.'):
|
||||
raise exceptions.IgnoredEvent
|
||||
if event.startswith('libvirt.'):
|
||||
raise exceptions.IgnoredEvent
|
||||
if event.startswith('metrics.'):
|
||||
raise exceptions.IgnoredEvent
|
||||
if event.startswith('scheduler.'):
|
||||
raise exceptions.IgnoredEvent
|
||||
if event.startswith('server_group.'):
|
||||
raise exceptions.IgnoredEvent
|
||||
if event.startswith('service.'):
|
||||
raise exceptions.IgnoredEvent
|
||||
if event == 'volume.usage':
|
||||
raise exceptions.IgnoredEvent
|
||||
|
||||
raise exceptions.UnsupportedEventType
|
||||
|
||||
|
||||
|
@ -2,6 +2,8 @@ before_after
|
||||
flake8
|
||||
freezegun
|
||||
pylint
|
||||
pylint-flask
|
||||
pylint-flask-sqlalchemy
|
||||
pytest
|
||||
pytest-cov
|
||||
pytest-flask
|
||||
|
7
tox.ini
7
tox.ini
@ -21,8 +21,11 @@ commands = {posargs}
|
||||
|
||||
[testenv:linters]
|
||||
commands =
|
||||
pylint atmosphere
|
||||
flake8 atmosphere
|
||||
pylint atmosphere \
|
||||
--load-plugins pylint_flask,pylint_flask_sqlalchemy \
|
||||
--ignore migrations,tests
|
||||
flake8 atmosphere \
|
||||
--exclude .tox,atmosphere/migrations,atmosphere/tests
|
||||
|
||||
[testenv:docs]
|
||||
deps =
|
||||
|
Loading…
Reference in New Issue
Block a user