add a new service list api

Story: 2004897
Task: 29204
Task: 29207
Depends-On: I2996afcd4f05c87847db1f9be64a362a2593f5b6
Change-Id: I494e5749adc1d3845f793a941f970a944457d992
This commit is contained in:
Eyal 2019-02-03 18:10:17 +02:00
parent f87cdcd111
commit 12f70b9ada
29 changed files with 543 additions and 32 deletions

View File

@ -216,6 +216,12 @@ function configure_vitrage {
else else
write_uwsgi_config "$VITRAGE_UWSGI_FILE" "$VITRAGE_PUBLIC_UWSGI" "/rca" write_uwsgi_config "$VITRAGE_UWSGI_FILE" "$VITRAGE_PUBLIC_UWSGI" "/rca"
fi fi
if [[ ! -z "$VITRAGE_COORDINATION_URL" ]]; then
iniset $VITRAGE_CONF coordination backend_url "$VITRAGE_COORDINATION_URL"
elif is_service_enabled etcd3; then
iniset $VITRAGE_CONF coordination backend_url "etcd3://${SERVICE_HOST}:$ETCD_PORT"
fi
} }
# init_vitrage() - Initialize etc. # init_vitrage() - Initialize etc.

View File

@ -1783,7 +1783,7 @@ Request Examples
:: ::
DELETE /v1/resources/`<id>` DELETE /v1/webhook/`<id>`
Host: 127.0.0.1:8999 Host: 127.0.0.1:8999
User-Agent: keystoneauth1/2.3.0 python-requests/2.9.1 CPython/2.7.6 User-Agent: keystoneauth1/2.3.0 python-requests/2.9.1 CPython/2.7.6
Accept: application/json Accept: application/json
@ -1799,4 +1799,133 @@ Response Body
============= =============
Returns a success message if the webhook is deleted, otherwise an error Returns a success message if the webhook is deleted, otherwise an error
message is returned. message is returned.
Service list
^^^^^^^^^^^^
Lists the vitrage services present in the system
GET /v1/services/
~~~~~~~~~~~~~~~~~~
Headers
=======
- X-Auth-Token (string, required) - Keystone auth token
- Accept (string) - application/json
Path Parameters
===============
None.
Query Parameters
================
None.
Request Body
============
None.
Request Examples
================
::
GET //v1/services/ HTTP/1.1
Host: 135.248.19.18:8999
X-Auth-Token: 2b8882ba2ec44295bf300aecb2caa4f7
Accept: application/json
ResponseStatus code
===================
- 200 - OK
- 404 - Not Found
- 500 - Service API not supported
- 500 - Failed to connect to coordination backend
Response Body
=============
Returns a JSON object with a list of all services.
Response Examples
=================
::
[
{
"Created At": "2019-02-10T11:07:15+00:00",
"Hostname": "controller-1",
"Process Id": 23161,
"Name": "ApiWorker worker(0)"
},
{
"Created At": "2019-02-10T11:07:15+00:00",
"Hostname": "controller-1",
"Process Id": 23153,
"Name": "EvaluatorWorker worker(0)"
},
{
"Created At": "2019-02-10T11:07:15+00:00",
"Hostname": "controller-1",
"Process Id": 23155,
"Name": "EvaluatorWorker worker(1)"
},
{
"Created At": "2019-02-10T11:07:15+00:00",
"Hostname": "controller-1",
"Process Id": 23157,
"Name": "EvaluatorWorker worker(2)"
},
{
"Created At": "2019-02-10T11:07:15+00:00",
"Hostname": "controller-1",
"Process Id": 23158,
"Name": "EvaluatorWorker worker(3)"
},
{
"Created At": "2019-02-10T11:07:33+00:00",
"Hostname": "controller-1",
"Process Id": 23366,
"Name": "MachineLearningService worker(0)"
},
{
"Created At": "2019-02-10T11:07:35+00:00",
"Hostname": "controller-1",
"Process Id": 23475,
"Name": "PersistorService worker(0)"
},
{
"Created At": "2019-02-10T11:07:15+00:00",
"Hostname": "controller-1",
"Process Id": 23164,
"Name": "SnmpParsingService worker(0)"
},
{
"Created At": "2019-02-10T11:14:30+00:00",
"Hostname": "controller-1",
"Process Id": 25698,
"Name": "VitrageApi"
},
{
"Created At": "2019-02-10T11:14:30+00:00",
"Hostname": "controller-1",
"Process Id": 25699,
"Name": "VitrageApi"
},
{
"Created At": "2019-02-10T11:07:32+00:00",
"Hostname": "controller-1",
"Process Id": 23352,
"Name": "VitrageNotifierService worker(0)"
}
]

View File

@ -142,3 +142,6 @@ wrapt==1.10.11
futures==3.0.0 futures==3.0.0
docutils==0.11 docutils==0.11
python-zaqarclient==1.2.0 python-zaqarclient==1.2.0
tooz==1.58.0
zake==0.1.6
psutil==5.4.3

View File

@ -1,6 +0,0 @@
[DEFAULT]
# The list of modules to copy from oslo-incubator.git
# The base module to hold the copy of openstack.common
base=vitrage

View File

@ -0,0 +1,3 @@
---
features:
- Added a new API to list all vitrage services present in the system.

View File

@ -52,3 +52,5 @@ cotyledon>=1.6.8 # Apache-2.0
futures>=3.0.0;python_version=='2.7' or python_version=='2.6' # BSD futures>=3.0.0;python_version=='2.7' or python_version=='2.6' # BSD
pytz>=2013.6 # MIT pytz>=2013.6 # MIT
tenacity>=4.9.0 tenacity>=4.9.0
tooz>=1.58.0 # Apache-2.0
psutil>=5.4.3 # BSD

View File

@ -12,3 +12,4 @@ testtools>=2.3.0 # MIT
stestr>=2.0.0 # Apache-2.0 stestr>=2.0.0 # Apache-2.0
reno>=2.7.0 # Apache-2.0 reno>=2.7.0 # Apache-2.0
mock>=2.0.0 # BSD mock>=2.0.0 # BSD
zake>=0.1.6 # Apache-2.0

View File

@ -41,7 +41,8 @@ def setup_app(root, conf=None):
hooks.GCHook(), hooks.GCHook(),
hooks.RPCHook(conf), hooks.RPCHook(conf),
hooks.ContextHook(), hooks.ContextHook(),
hooks.DBHook(conf)] hooks.DBHook(conf),
hooks.CoordinatorHook(conf)]
app = pecan.make_app( app = pecan.make_app(
root, root,

View File

@ -15,6 +15,7 @@ from vitrage.api.controllers.v1 import alarm
from vitrage.api.controllers.v1 import event from vitrage.api.controllers.v1 import event
from vitrage.api.controllers.v1 import rca from vitrage.api.controllers.v1 import rca
from vitrage.api.controllers.v1 import resource from vitrage.api.controllers.v1 import resource
from vitrage.api.controllers.v1 import service
from vitrage.api.controllers.v1 import template from vitrage.api.controllers.v1 import template
from vitrage.api.controllers.v1 import topology from vitrage.api.controllers.v1 import topology
from vitrage.api.controllers.v1 import webhook from vitrage.api.controllers.v1 import webhook
@ -31,3 +32,4 @@ class V1Controller(object):
webhook = webhook.WebhookController() webhook = webhook.WebhookController()
template = template.TemplateController() template = template.TemplateController()
event = event.EventController() event = event.EventController()
service = service.ServiceController()

View File

@ -0,0 +1,49 @@
# Copyright 2019 - Nokia Corporation
#
# 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 operator import itemgetter
from oslo_log import log
import pecan
from pecan.core import abort
from vitrage.api.policy import enforce
LOG = log.getLogger(__name__)
# noinspection PyBroadException
class ServiceController(object):
@pecan.expose('json')
def index(self):
return self.get()
@pecan.expose('json')
def get(self):
enforce("get service list", pecan.request.headers,
pecan.request.enforcer, {})
LOG.info('received get service list')
coordinator = pecan.request.coordinator
if not coordinator.backend_url:
abort(500, 'Service API not supported')
if not coordinator.is_active():
abort(500, 'Failed to connect to coordination backend')
try:
return sorted(coordinator.get_services(), key=itemgetter('name'))
except Exception:
LOG.exception('failed to get service list.')
abort(404, 'Failed to get service list.')

View File

@ -17,6 +17,7 @@ from oslo_policy import policy
from pecan import hooks from pecan import hooks
from vitrage.common import policies from vitrage.common import policies
from vitrage.coordination import coordination
from vitrage import messaging from vitrage import messaging
from vitrage import rpc as vitrage_rpc from vitrage import rpc as vitrage_rpc
from vitrage import storage from vitrage import storage
@ -95,3 +96,14 @@ class GCHook(hooks.PecanHook):
def after(self, state): def after(self, state):
gc.collect() gc.collect()
class CoordinatorHook(hooks.PecanHook):
def __init__(self, conf):
self.coordinator = coordination.Coordinator(conf)
self.coordinator.start()
self.coordinator.join_group()
def before(self, state):
state.request.coordinator = self.coordinator

View File

@ -16,6 +16,7 @@ from vitrage.common.policies import alarms
from vitrage.common.policies import event from vitrage.common.policies import event
from vitrage.common.policies import rca from vitrage.common.policies import rca
from vitrage.common.policies import resource from vitrage.common.policies import resource
from vitrage.common.policies import service
from vitrage.common.policies import template from vitrage.common.policies import template
from vitrage.common.policies import topology from vitrage.common.policies import topology
from vitrage.common.policies import webhook from vitrage.common.policies import webhook
@ -29,5 +30,6 @@ def list_rules():
template.list_rules(), template.list_rules(),
topology.list_rules(), topology.list_rules(),
resource.list_rules(), resource.list_rules(),
webhook.list_rules() webhook.list_rules(),
service.list_rules(),
) )

View File

@ -0,0 +1,37 @@
# Copyright 2019 - Nokia Corporation
#
# 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 oslo_policy import policy
from vitrage.common.policies import base
SERVICE = 'get service list'
rules = [
policy.DocumentedRuleDefault(
name=SERVICE,
check_str=base.UNPROTECTED,
description='Get the vitrage services for the OpenStack cluster',
operations=[
{
'path': '/service',
'method': 'GET'
}
]
)
]
def list_rules():
return rules

View File

@ -0,0 +1,21 @@
# Copyright 2019 - Nokia Corporation
#
# 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 oslo_config import cfg
OPTS = [
cfg.StrOpt('backend_url',
help='The backend URL to use for the coordination service. If '
'left empty, membership api will not work')
]

View File

@ -0,0 +1,113 @@
# Copyright 2019 - Nokia Corporation
#
# 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 json
import os
import psutil
import socket
import six
import tenacity
import tooz.coordination
from oslo_log import log
from oslo_utils import timeutils
LOG = log.getLogger(__name__)
class Coordinator(object):
def __init__(self, conf, my_id=None):
self.conf = conf
self.backend_url = self.conf.coordination.backend_url
self.my_id = my_id or ' '.join(psutil.Process(os.getpid()).cmdline())
self.coordinator = None
if self.backend_url:
self.coordinator = tooz.coordination.get_coordinator(
self.backend_url, six.b('%s_%s' % (my_id, os.getpid())))
def start(self):
if self.backend_url:
try:
self.coordinator.start(start_heart=True)
LOG.info('Coordination backend started successfully.')
except tooz.coordination.ToozError:
LOG.exception('Error connecting to coordination backend.')
def stop(self):
if not self.is_active():
return
try:
self.coordinator.stop()
except tooz.coordination.ToozError:
LOG.exception('Error connecting to coordination backend.')
def is_active(self):
return self.coordinator and self.coordinator.is_started
@tenacity.retry(stop=tenacity.stop_after_attempt(5))
def join_group(self, group_id='vitrage'):
if not self.is_active() or not group_id:
return
try:
now = timeutils.utcnow(with_timezone=True).replace(microsecond=0)
isoformat = now.isoformat()
capabilities = json.dumps(
{
'name': self.my_id,
'hostname': socket.gethostname(),
'process': os.getpid(),
'created': isoformat
}
)
join_req = self.coordinator.join_group(six.b(group_id),
six.b(capabilities))
join_req.get()
LOG.info('Joined service group:%s, member:%s',
group_id, self.my_id)
return
except tooz.coordination.MemberAlreadyExist:
return
except tooz.coordination.GroupNotCreated as e:
create_grp_req = self.coordinator.create_group(six.b(group_id))
try:
create_grp_req.get()
except tooz.coordination.GroupAlreadyExist:
pass
# Re-raise exception to join group again.
raise e
def leave_group(self, group_id):
if self.is_active():
self.coordinator.leave_group(six.b(group_id))
LOG.info('Left group %s', group_id)
def get_services(self, group_id='vitrage'):
if not self.is_active():
return []
while True:
get_members_req = self.coordinator.get_members(six.b(group_id))
try:
return [json.loads(
self.coordinator.get_member_capabilities(
six.b(group_id), member).get().decode('us-ascii'))
for member in get_members_req.get()]
except tooz.coordination.GroupNotCreated:
self.join_group(group_id)

View File

@ -0,0 +1,32 @@
# Copyright 2019 - Nokia Corporation
#
# 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 cotyledon
from vitrage.coordination.coordination import Coordinator
class Service(cotyledon.Service):
def __init__(self, worker_id, conf):
super(Service, self).__init__(worker_id)
self.coordinator = Coordinator(conf, '%s worker(%s)' % (self.name,
worker_id))
def run(self):
self.coordinator.start()
self.coordinator.join_group()
def terminate(self):
self.coordinator.stop()

View File

@ -36,6 +36,7 @@ from vitrage.api_handler.apis.webhook import WebhookApis
from vitrage.common.constants import TemplateStatus as TStatus from vitrage.common.constants import TemplateStatus as TStatus
from vitrage.common.constants import TemplateTypes as TType from vitrage.common.constants import TemplateTypes as TType
from vitrage.common.exception import VitrageError from vitrage.common.exception import VitrageError
from vitrage.coordination import service as coord
from vitrage.entity_graph import EVALUATOR_TOPIC from vitrage.entity_graph import EVALUATOR_TOPIC
from vitrage.evaluator.actions.base import ActionMode from vitrage.evaluator.actions.base import ActionMode
from vitrage.evaluator.scenario_evaluator import ScenarioEvaluator from vitrage.evaluator.scenario_evaluator import ScenarioEvaluator
@ -214,12 +215,12 @@ class GraphWorkersManager(cotyledon.ServiceManager):
os._exit(0) os._exit(0)
class GraphCloneWorkerBase(cotyledon.Service): class GraphCloneWorkerBase(coord.Service):
def __init__(self, def __init__(self,
worker_id, worker_id,
conf, conf,
task_queues): task_queues):
super(GraphCloneWorkerBase, self).__init__(worker_id) super(GraphCloneWorkerBase, self).__init__(worker_id, conf)
self._conf = conf self._conf = conf
self._task_queue = task_queues[worker_id] self._task_queue = task_queues[worker_id]
self._entity_graph = NXGraph() self._entity_graph = NXGraph()
@ -232,6 +233,7 @@ class GraphCloneWorkerBase(cotyledon.Service):
raise NotImplementedError raise NotImplementedError
def run(self): def run(self):
super(GraphCloneWorkerBase, self).run()
self._entity_graph.notifier._subscriptions = [] # Quick n dirty self._entity_graph.notifier._subscriptions = [] # Quick n dirty
self._init_instance() self._init_instance()
if self._entity_graph.num_vertices(): if self._entity_graph.num_vertices():

View File

@ -12,22 +12,22 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import cotyledon
from oslo_log import log from oslo_log import log
import oslo_messaging as oslo_m import oslo_messaging as oslo_m
from oslo_utils import importutils from oslo_utils import importutils
from vitrage.coordination import service as coord
from vitrage import messaging from vitrage import messaging
from vitrage.opts import register_opts from vitrage.opts import register_opts
LOG = log.getLogger(__name__) LOG = log.getLogger(__name__)
class MachineLearningService(cotyledon.Service): class MachineLearningService(coord.Service):
def __init__(self, worker_id, conf): def __init__(self, worker_id, conf):
super(MachineLearningService, self).__init__(worker_id) super(MachineLearningService, self).__init__(worker_id, conf)
self.conf = conf self.conf = conf
self.machine_learning_plugins = self.get_machine_learning_plugins(conf) self.machine_learning_plugins = self.get_machine_learning_plugins(conf)
transport = messaging.get_transport(conf) transport = messaging.get_transport(conf)
@ -38,6 +38,7 @@ class MachineLearningService(cotyledon.Service):
[VitrageEventEndpoint(self.machine_learning_plugins)]) [VitrageEventEndpoint(self.machine_learning_plugins)])
def run(self): def run(self):
super(MachineLearningService, self).run()
LOG.info("Vitrage Machine Learning Service - Starting...") LOG.info("Vitrage Machine Learning Service - Starting...")
self.listener.start() self.listener.start()
@ -45,6 +46,7 @@ class MachineLearningService(cotyledon.Service):
LOG.info("Vitrage Machine Learning Service - Started!") LOG.info("Vitrage Machine Learning Service - Started!")
def terminate(self): def terminate(self):
super(MachineLearningService, self).terminate()
LOG.info("Vitrage Machine Learning Service - Stopping...") LOG.info("Vitrage Machine Learning Service - Stopping...")
self.listener.stop() self.listener.stop()

View File

@ -11,26 +11,28 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import cotyledon
from oslo_log import log from oslo_log import log
import oslo_messaging import oslo_messaging
from oslo_utils import importutils from oslo_utils import importutils
from vitrage.coordination import service as coord
from vitrage import messaging from vitrage import messaging
from vitrage.opts import register_opts from vitrage.opts import register_opts
LOG = log.getLogger(__name__) LOG = log.getLogger(__name__)
class VitrageNotifierService(cotyledon.Service): class VitrageNotifierService(coord.Service):
def __init__(self, worker_id, conf): def __init__(self, worker_id, conf):
super(VitrageNotifierService, self).__init__(worker_id) super(VitrageNotifierService, self).__init__(worker_id, conf)
self.conf = conf self.conf = conf
self.notifiers = self.get_notifier_plugins(conf) self.notifiers = self.get_notifier_plugins(conf)
self._init_listeners(self.conf) self._init_listeners(self.conf)
def run(self): def run(self):
super(VitrageNotifierService, self).run()
LOG.info("Vitrage Notifier Service - Starting...") LOG.info("Vitrage Notifier Service - Starting...")
for listener in self.listeners: for listener in self.listeners:
@ -39,6 +41,7 @@ class VitrageNotifierService(cotyledon.Service):
LOG.info("Vitrage Notifier Service - Started!") LOG.info("Vitrage Notifier Service - Started!")
def terminate(self): def terminate(self):
super(VitrageNotifierService, self).terminate()
LOG.info("Vitrage Notifier Service - Stopping...") LOG.info("Vitrage Notifier Service - Stopping...")
for listener in self.listeners: for listener in self.listeners:

View File

@ -19,6 +19,7 @@ from oslo_log import log
from oslo_utils import importutils from oslo_utils import importutils
import vitrage.api import vitrage.api
import vitrage.coordination
import vitrage.datasources import vitrage.datasources
import vitrage.entity_graph.consistency import vitrage.entity_graph.consistency
import vitrage.evaluator import vitrage.evaluator
@ -63,6 +64,7 @@ def list_opts():
('webhook', vitrage.notifier.plugins.webhook.OPTS), ('webhook', vitrage.notifier.plugins.webhook.OPTS),
('snmp_parsing', vitrage.snmp_parsing.OPTS), ('snmp_parsing', vitrage.snmp_parsing.OPTS),
('zaqar', vitrage.notifier.plugins.zaqar.OPTS), ('zaqar', vitrage.notifier.plugins.zaqar.OPTS),
('coordination', vitrage.coordination.OPTS),
('DEFAULT', itertools.chain( ('DEFAULT', itertools.chain(
vitrage.os_clients.OPTS, vitrage.os_clients.OPTS,
vitrage.rpc.OPTS, vitrage.rpc.OPTS,

View File

@ -17,7 +17,6 @@ from __future__ import print_function
from datetime import timedelta from datetime import timedelta
from concurrent.futures import ThreadPoolExecutor from concurrent.futures import ThreadPoolExecutor
import cotyledon
import dateutil.parser import dateutil.parser
from futurist import periodics from futurist import periodics
@ -31,6 +30,7 @@ from vitrage.common.constants import HistoryProps as HProps
from vitrage.common.constants import NotifierEventTypes as NETypes from vitrage.common.constants import NotifierEventTypes as NETypes
from vitrage.common.constants import VertexProperties as VProps from vitrage.common.constants import VertexProperties as VProps
from vitrage.common.utils import spawn from vitrage.common.utils import spawn
from vitrage.coordination import service as coord
from vitrage import messaging from vitrage import messaging
from vitrage.storage.sqlalchemy import models from vitrage.storage.sqlalchemy import models
from vitrage.utils.datetime import utcnow from vitrage.utils.datetime import utcnow
@ -38,9 +38,9 @@ from vitrage.utils.datetime import utcnow
LOG = log.getLogger(__name__) LOG = log.getLogger(__name__)
class PersistorService(cotyledon.Service): class PersistorService(coord.Service):
def __init__(self, worker_id, conf, db_connection): def __init__(self, worker_id, conf, db_connection):
super(PersistorService, self).__init__(worker_id) super(PersistorService, self).__init__(worker_id, conf)
self.conf = conf self.conf = conf
self.db_connection = db_connection self.db_connection = db_connection
transport = messaging.get_transport(conf) transport = messaging.get_transport(conf)
@ -52,6 +52,7 @@ class PersistorService(cotyledon.Service):
self.scheduler = Scheduler(conf, db_connection) self.scheduler = Scheduler(conf, db_connection)
def run(self): def run(self):
super(PersistorService, self).run()
LOG.info("Vitrage Persistor Service - Starting...") LOG.info("Vitrage Persistor Service - Starting...")
self.listener.start() self.listener.start()
@ -60,6 +61,7 @@ class PersistorService(cotyledon.Service):
LOG.info("Vitrage Persistor Service - Started!") LOG.info("Vitrage Persistor Service - Started!")
def terminate(self): def terminate(self):
super(PersistorService, self).terminate()
LOG.info("Vitrage Persistor Service - Stopping...") LOG.info("Vitrage Persistor Service - Stopping...")
self.listener.stop() self.listener.stop()

View File

@ -15,7 +15,6 @@
from datetime import datetime from datetime import datetime
import json import json
import cotyledon
from oslo_log import log from oslo_log import log
import oslo_messaging import oslo_messaging
from oslo_utils import uuidutils from oslo_utils import uuidutils
@ -28,6 +27,7 @@ from pysnmp.proto.rfc1902 import Integer
import sys import sys
from vitrage.common.constants import EventProperties from vitrage.common.constants import EventProperties
from vitrage.coordination import service as coord
from vitrage.datasources.transformer_base import extract_field_value from vitrage.datasources.transformer_base import extract_field_value
from vitrage.messaging import get_transport from vitrage.messaging import get_transport
from vitrage.snmp_parsing.properties import SnmpEventProperties as SEProps from vitrage.snmp_parsing.properties import SnmpEventProperties as SEProps
@ -36,16 +36,17 @@ from vitrage.utils.file import load_yaml_file
LOG = log.getLogger(__name__) LOG = log.getLogger(__name__)
class SnmpParsingService(cotyledon.Service): class SnmpParsingService(coord.Service):
RUN_FOREVER = 1 RUN_FOREVER = 1
def __init__(self, worker_id, conf): def __init__(self, worker_id, conf):
super(SnmpParsingService, self).__init__(worker_id) super(SnmpParsingService, self).__init__(worker_id, conf)
self.conf = conf self.conf = conf
self.listening_port = conf.snmp_parsing.snmp_listening_port self.listening_port = conf.snmp_parsing.snmp_listening_port
self._init_oslo_notifier() self._init_oslo_notifier()
def run(self): def run(self):
super(SnmpParsingService, self).run()
LOG.info("Vitrage SNMP Parsing Service - Starting...") LOG.info("Vitrage SNMP Parsing Service - Starting...")
transport_dispatcher = AsyncoreDispatcher() transport_dispatcher = AsyncoreDispatcher()
@ -72,6 +73,7 @@ class SnmpParsingService(cotyledon.Service):
raise raise
def terminate(self): def terminate(self):
super(SnmpParsingService, self).terminate()
LOG.info("Vitrage SNMP Parsing Service - Stopping...") LOG.info("Vitrage SNMP Parsing Service - Stopping...")
LOG.info("Vitrage SNMP Parsing Service - Stopped!") LOG.info("Vitrage SNMP Parsing Service - Stopped!")

View File

@ -20,7 +20,8 @@ import uuid
from keystonemiddleware import fixture as ksm_fixture from keystonemiddleware import fixture as ksm_fixture
from mock import mock # noinspection PyPackageRequirements
import mock
from vitrage.tests.functional.api.v1 import FunctionalTest from vitrage.tests.functional.api.v1 import FunctionalTest
EVENT_DETAILS = { EVENT_DETAILS = {

View File

@ -18,7 +18,8 @@
import uuid import uuid
from datetime import datetime from datetime import datetime
from mock import mock # noinspection PyPackageRequirements
import mock
from six.moves import http_client as httplib from six.moves import http_client as httplib
from vitrage.tests.functional.api.v1 import FunctionalTest from vitrage.tests.functional.api.v1 import FunctionalTest

View File

@ -14,9 +14,9 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
# noinspection PyPackageRequirements
from datetime import datetime from datetime import datetime
from mock import mock # noinspection PyPackageRequirements
import mock
import requests_mock import requests_mock
from vitrage.middleware.keycloak import KeycloakAuth from vitrage.middleware.keycloak import KeycloakAuth
from vitrage.tests.functional.api.v1 import FunctionalTest from vitrage.tests.functional.api.v1 import FunctionalTest

View File

@ -17,7 +17,7 @@
from datetime import datetime from datetime import datetime
# noinspection PyPackageRequirements # noinspection PyPackageRequirements
from mock import mock import mock
from vitrage.common.utils import compress_obj from vitrage.common.utils import compress_obj
from vitrage.storage.sqlalchemy import models from vitrage.storage.sqlalchemy import models

View File

@ -0,0 +1,84 @@
# Copyright 2019 - Nokia Corporation
#
# 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 datetime import datetime
import os
import socket
# noinspection PyPackageRequirements
from iso8601.iso8601 import UTC
# noinspection PyPackageRequirements
import mock
from oslo_utils import timeutils
# noinspection PyPackageRequirements
import webtest
from vitrage.api import app
from vitrage.coordination.coordination import Coordinator
from vitrage.tests.functional.api.v1 import FunctionalTest
class ServiceTest(FunctionalTest):
SERVICE_CREATION_TIME = datetime(2015, 1, 26, 12, 57, 4, tzinfo=UTC)
def __init__(self, *args, **kwds):
super(ServiceTest, self).__init__(*args, **kwds)
self.auth = 'noauth'
def test_get_services_no_backend(self):
resp = self.get_json('/service/', expect_errors=True)
self.assertEqual(500, resp.status_code)
self.assertIn('Service API not supported', resp.text)
def test_get_services_no_connection_to_backend(self):
self._use_zake_as_backend()
with mock.patch('pecan.request') as request:
request.coordinator.is_active.return_value = False
resp = self.get_json('/service/', expect_errors=True)
self.assertEqual(500, resp.status_code)
self.assertIn('Failed to connect to coordination backend',
resp.text)
@mock.patch.object(timeutils, 'utcnow')
def test_get_services(self, utcnow):
now = self._mock_service_creation_time(utcnow)
# NOTE(eyalb) we want to force coordinator to be initialized with a
# custom name otherwise it will take the command line string as a name
name = 'vitrage'
with mock.patch('vitrage.coordination.coordination.Coordinator',
new=lambda _: Coordinator(self.CONF, name)):
self._use_zake_as_backend()
data = self.get_json('/service/')
self.assert_list_equal([
{
'name': name,
'hostname': socket.gethostname(),
'process': os.getpid(),
'created': now
}
], data)
def _mock_service_creation_time(self, utcnow):
utcnow.return_value = self.SERVICE_CREATION_TIME
return utcnow.return_value.isoformat()
# noinspection PyAttributeOutsideInit
def _use_zake_as_backend(self):
self.CONF.set_override('backend_url', 'zake://', 'coordination')
self.app = webtest.TestApp(app.load_app(self.CONF))

View File

@ -12,8 +12,10 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from mock import mock # noinspection PyPackageRequirements
import mock
from oslo_config import cfg from oslo_config import cfg
# noinspection PyPackageRequirements
from testtools import matchers from testtools import matchers
from vitrage.common.constants import DatasourceOpts as DSOpts from vitrage.common.constants import DatasourceOpts as DSOpts
@ -61,12 +63,12 @@ class PrometheusDriverTest(base.BaseTest):
# Test Action # Test Action
observed_valid_ip = driver._adjust_label_value(valid_ip) observed_valid_ip = driver._adjust_label_value(valid_ip)
observed_not_ip = driver._adjust_label_value(not_ip) observed_not_ip = driver._adjust_label_value(not_ip)
observed_unvalid_ip = driver._adjust_label_value(invalid_ip) observed_invalid_ip = driver._adjust_label_value(invalid_ip)
# Test assertions # Test assertions
self.assertEqual(hostname, observed_valid_ip) self.assertEqual(hostname, observed_valid_ip)
self.assertEqual(not_ip, observed_not_ip) self.assertEqual(not_ip, observed_not_ip)
self.assertEqual(invalid_ip, observed_unvalid_ip) self.assertEqual(invalid_ip, observed_invalid_ip)
@mock.patch('socket.gethostbyaddr') @mock.patch('socket.gethostbyaddr')
def test_calculate_host_vitrage_entity_unique_props(self, mock_socket): def test_calculate_host_vitrage_entity_unique_props(self, mock_socket):

View File

@ -124,6 +124,9 @@ class TestSnmpParsing(base.BaseTest):
super(TestSnmpParsing, cls).setUpClass() super(TestSnmpParsing, cls).setUpClass()
cls.conf = cfg.ConfigOpts() cls.conf = cfg.ConfigOpts()
cls.conf.register_opts(cls.OPTS, group='snmp_parsing') cls.conf.register_opts(cls.OPTS, group='snmp_parsing')
cls.conf.register_opt(
cfg.StrOpt('backend_url', default='zake://'), group='coordination'
)
def test_convert_binds_to_dict(self): def test_convert_binds_to_dict(self):
parsing_service = SnmpParsingService(1, self.conf) parsing_service = SnmpParsingService(1, self.conf)