add a new service list api
Story: 2004897 Task: 29204 Task: 29207 Depends-On: I2996afcd4f05c87847db1f9be64a362a2593f5b6 Change-Id: I494e5749adc1d3845f793a941f970a944457d992
This commit is contained in:
parent
f87cdcd111
commit
12f70b9ada
@ -216,6 +216,12 @@ function configure_vitrage {
|
||||
else
|
||||
write_uwsgi_config "$VITRAGE_UWSGI_FILE" "$VITRAGE_PUBLIC_UWSGI" "/rca"
|
||||
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.
|
||||
|
@ -1783,7 +1783,7 @@ Request Examples
|
||||
|
||||
::
|
||||
|
||||
DELETE /v1/resources/`<id>`
|
||||
DELETE /v1/webhook/`<id>`
|
||||
Host: 127.0.0.1:8999
|
||||
User-Agent: keystoneauth1/2.3.0 python-requests/2.9.1 CPython/2.7.6
|
||||
Accept: application/json
|
||||
@ -1800,3 +1800,132 @@ Response Body
|
||||
|
||||
Returns a success message if the webhook is deleted, otherwise an error
|
||||
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)"
|
||||
}
|
||||
]
|
||||
|
@ -142,3 +142,6 @@ wrapt==1.10.11
|
||||
futures==3.0.0
|
||||
docutils==0.11
|
||||
python-zaqarclient==1.2.0
|
||||
tooz==1.58.0
|
||||
zake==0.1.6
|
||||
psutil==5.4.3
|
||||
|
@ -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
|
@ -0,0 +1,3 @@
|
||||
---
|
||||
features:
|
||||
- Added a new API to list all vitrage services present in the system.
|
@ -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
|
||||
pytz>=2013.6 # MIT
|
||||
tenacity>=4.9.0
|
||||
tooz>=1.58.0 # Apache-2.0
|
||||
psutil>=5.4.3 # BSD
|
@ -12,3 +12,4 @@ testtools>=2.3.0 # MIT
|
||||
stestr>=2.0.0 # Apache-2.0
|
||||
reno>=2.7.0 # Apache-2.0
|
||||
mock>=2.0.0 # BSD
|
||||
zake>=0.1.6 # Apache-2.0
|
||||
|
@ -41,7 +41,8 @@ def setup_app(root, conf=None):
|
||||
hooks.GCHook(),
|
||||
hooks.RPCHook(conf),
|
||||
hooks.ContextHook(),
|
||||
hooks.DBHook(conf)]
|
||||
hooks.DBHook(conf),
|
||||
hooks.CoordinatorHook(conf)]
|
||||
|
||||
app = pecan.make_app(
|
||||
root,
|
||||
|
@ -15,6 +15,7 @@ from vitrage.api.controllers.v1 import alarm
|
||||
from vitrage.api.controllers.v1 import event
|
||||
from vitrage.api.controllers.v1 import rca
|
||||
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 topology
|
||||
from vitrage.api.controllers.v1 import webhook
|
||||
@ -31,3 +32,4 @@ class V1Controller(object):
|
||||
webhook = webhook.WebhookController()
|
||||
template = template.TemplateController()
|
||||
event = event.EventController()
|
||||
service = service.ServiceController()
|
||||
|
49
vitrage/api/controllers/v1/service.py
Normal file
49
vitrage/api/controllers/v1/service.py
Normal 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.')
|
@ -17,6 +17,7 @@ from oslo_policy import policy
|
||||
from pecan import hooks
|
||||
|
||||
from vitrage.common import policies
|
||||
from vitrage.coordination import coordination
|
||||
from vitrage import messaging
|
||||
from vitrage import rpc as vitrage_rpc
|
||||
from vitrage import storage
|
||||
@ -95,3 +96,14 @@ class GCHook(hooks.PecanHook):
|
||||
|
||||
def after(self, state):
|
||||
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
|
||||
|
@ -16,6 +16,7 @@ from vitrage.common.policies import alarms
|
||||
from vitrage.common.policies import event
|
||||
from vitrage.common.policies import rca
|
||||
from vitrage.common.policies import resource
|
||||
from vitrage.common.policies import service
|
||||
from vitrage.common.policies import template
|
||||
from vitrage.common.policies import topology
|
||||
from vitrage.common.policies import webhook
|
||||
@ -29,5 +30,6 @@ def list_rules():
|
||||
template.list_rules(),
|
||||
topology.list_rules(),
|
||||
resource.list_rules(),
|
||||
webhook.list_rules()
|
||||
webhook.list_rules(),
|
||||
service.list_rules(),
|
||||
)
|
||||
|
37
vitrage/common/policies/service.py
Normal file
37
vitrage/common/policies/service.py
Normal 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
|
21
vitrage/coordination/__init__.py
Normal file
21
vitrage/coordination/__init__.py
Normal 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')
|
||||
]
|
113
vitrage/coordination/coordination.py
Normal file
113
vitrage/coordination/coordination.py
Normal 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)
|
32
vitrage/coordination/service.py
Normal file
32
vitrage/coordination/service.py
Normal 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()
|
@ -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 TemplateTypes as TType
|
||||
from vitrage.common.exception import VitrageError
|
||||
from vitrage.coordination import service as coord
|
||||
from vitrage.entity_graph import EVALUATOR_TOPIC
|
||||
from vitrage.evaluator.actions.base import ActionMode
|
||||
from vitrage.evaluator.scenario_evaluator import ScenarioEvaluator
|
||||
@ -214,12 +215,12 @@ class GraphWorkersManager(cotyledon.ServiceManager):
|
||||
os._exit(0)
|
||||
|
||||
|
||||
class GraphCloneWorkerBase(cotyledon.Service):
|
||||
class GraphCloneWorkerBase(coord.Service):
|
||||
def __init__(self,
|
||||
worker_id,
|
||||
conf,
|
||||
task_queues):
|
||||
super(GraphCloneWorkerBase, self).__init__(worker_id)
|
||||
super(GraphCloneWorkerBase, self).__init__(worker_id, conf)
|
||||
self._conf = conf
|
||||
self._task_queue = task_queues[worker_id]
|
||||
self._entity_graph = NXGraph()
|
||||
@ -232,6 +233,7 @@ class GraphCloneWorkerBase(cotyledon.Service):
|
||||
raise NotImplementedError
|
||||
|
||||
def run(self):
|
||||
super(GraphCloneWorkerBase, self).run()
|
||||
self._entity_graph.notifier._subscriptions = [] # Quick n dirty
|
||||
self._init_instance()
|
||||
if self._entity_graph.num_vertices():
|
||||
|
@ -12,22 +12,22 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import cotyledon
|
||||
|
||||
from oslo_log import log
|
||||
import oslo_messaging as oslo_m
|
||||
from oslo_utils import importutils
|
||||
|
||||
from vitrage.coordination import service as coord
|
||||
from vitrage import messaging
|
||||
from vitrage.opts import register_opts
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
class MachineLearningService(cotyledon.Service):
|
||||
class MachineLearningService(coord.Service):
|
||||
|
||||
def __init__(self, worker_id, conf):
|
||||
super(MachineLearningService, self).__init__(worker_id)
|
||||
super(MachineLearningService, self).__init__(worker_id, conf)
|
||||
self.conf = conf
|
||||
self.machine_learning_plugins = self.get_machine_learning_plugins(conf)
|
||||
transport = messaging.get_transport(conf)
|
||||
@ -38,6 +38,7 @@ class MachineLearningService(cotyledon.Service):
|
||||
[VitrageEventEndpoint(self.machine_learning_plugins)])
|
||||
|
||||
def run(self):
|
||||
super(MachineLearningService, self).run()
|
||||
LOG.info("Vitrage Machine Learning Service - Starting...")
|
||||
|
||||
self.listener.start()
|
||||
@ -45,6 +46,7 @@ class MachineLearningService(cotyledon.Service):
|
||||
LOG.info("Vitrage Machine Learning Service - Started!")
|
||||
|
||||
def terminate(self):
|
||||
super(MachineLearningService, self).terminate()
|
||||
LOG.info("Vitrage Machine Learning Service - Stopping...")
|
||||
|
||||
self.listener.stop()
|
||||
|
@ -11,26 +11,28 @@
|
||||
# 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 oslo_log import log
|
||||
import oslo_messaging
|
||||
from oslo_utils import importutils
|
||||
|
||||
from vitrage.coordination import service as coord
|
||||
from vitrage import messaging
|
||||
from vitrage.opts import register_opts
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
class VitrageNotifierService(cotyledon.Service):
|
||||
class VitrageNotifierService(coord.Service):
|
||||
|
||||
def __init__(self, worker_id, conf):
|
||||
super(VitrageNotifierService, self).__init__(worker_id)
|
||||
super(VitrageNotifierService, self).__init__(worker_id, conf)
|
||||
self.conf = conf
|
||||
self.notifiers = self.get_notifier_plugins(conf)
|
||||
self._init_listeners(self.conf)
|
||||
|
||||
def run(self):
|
||||
super(VitrageNotifierService, self).run()
|
||||
|
||||
LOG.info("Vitrage Notifier Service - Starting...")
|
||||
|
||||
for listener in self.listeners:
|
||||
@ -39,6 +41,7 @@ class VitrageNotifierService(cotyledon.Service):
|
||||
LOG.info("Vitrage Notifier Service - Started!")
|
||||
|
||||
def terminate(self):
|
||||
super(VitrageNotifierService, self).terminate()
|
||||
LOG.info("Vitrage Notifier Service - Stopping...")
|
||||
|
||||
for listener in self.listeners:
|
||||
|
@ -19,6 +19,7 @@ from oslo_log import log
|
||||
from oslo_utils import importutils
|
||||
|
||||
import vitrage.api
|
||||
import vitrage.coordination
|
||||
import vitrage.datasources
|
||||
import vitrage.entity_graph.consistency
|
||||
import vitrage.evaluator
|
||||
@ -63,6 +64,7 @@ def list_opts():
|
||||
('webhook', vitrage.notifier.plugins.webhook.OPTS),
|
||||
('snmp_parsing', vitrage.snmp_parsing.OPTS),
|
||||
('zaqar', vitrage.notifier.plugins.zaqar.OPTS),
|
||||
('coordination', vitrage.coordination.OPTS),
|
||||
('DEFAULT', itertools.chain(
|
||||
vitrage.os_clients.OPTS,
|
||||
vitrage.rpc.OPTS,
|
||||
|
@ -17,7 +17,6 @@ from __future__ import print_function
|
||||
from datetime import timedelta
|
||||
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
import cotyledon
|
||||
import dateutil.parser
|
||||
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 VertexProperties as VProps
|
||||
from vitrage.common.utils import spawn
|
||||
from vitrage.coordination import service as coord
|
||||
from vitrage import messaging
|
||||
from vitrage.storage.sqlalchemy import models
|
||||
from vitrage.utils.datetime import utcnow
|
||||
@ -38,9 +38,9 @@ from vitrage.utils.datetime import utcnow
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
class PersistorService(cotyledon.Service):
|
||||
class PersistorService(coord.Service):
|
||||
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.db_connection = db_connection
|
||||
transport = messaging.get_transport(conf)
|
||||
@ -52,6 +52,7 @@ class PersistorService(cotyledon.Service):
|
||||
self.scheduler = Scheduler(conf, db_connection)
|
||||
|
||||
def run(self):
|
||||
super(PersistorService, self).run()
|
||||
LOG.info("Vitrage Persistor Service - Starting...")
|
||||
|
||||
self.listener.start()
|
||||
@ -60,6 +61,7 @@ class PersistorService(cotyledon.Service):
|
||||
LOG.info("Vitrage Persistor Service - Started!")
|
||||
|
||||
def terminate(self):
|
||||
super(PersistorService, self).terminate()
|
||||
LOG.info("Vitrage Persistor Service - Stopping...")
|
||||
|
||||
self.listener.stop()
|
||||
|
@ -15,7 +15,6 @@
|
||||
from datetime import datetime
|
||||
import json
|
||||
|
||||
import cotyledon
|
||||
from oslo_log import log
|
||||
import oslo_messaging
|
||||
from oslo_utils import uuidutils
|
||||
@ -28,6 +27,7 @@ from pysnmp.proto.rfc1902 import Integer
|
||||
import sys
|
||||
|
||||
from vitrage.common.constants import EventProperties
|
||||
from vitrage.coordination import service as coord
|
||||
from vitrage.datasources.transformer_base import extract_field_value
|
||||
from vitrage.messaging import get_transport
|
||||
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__)
|
||||
|
||||
|
||||
class SnmpParsingService(cotyledon.Service):
|
||||
class SnmpParsingService(coord.Service):
|
||||
RUN_FOREVER = 1
|
||||
|
||||
def __init__(self, worker_id, conf):
|
||||
super(SnmpParsingService, self).__init__(worker_id)
|
||||
super(SnmpParsingService, self).__init__(worker_id, conf)
|
||||
self.conf = conf
|
||||
self.listening_port = conf.snmp_parsing.snmp_listening_port
|
||||
self._init_oslo_notifier()
|
||||
|
||||
def run(self):
|
||||
super(SnmpParsingService, self).run()
|
||||
LOG.info("Vitrage SNMP Parsing Service - Starting...")
|
||||
|
||||
transport_dispatcher = AsyncoreDispatcher()
|
||||
@ -72,6 +73,7 @@ class SnmpParsingService(cotyledon.Service):
|
||||
raise
|
||||
|
||||
def terminate(self):
|
||||
super(SnmpParsingService, self).terminate()
|
||||
LOG.info("Vitrage SNMP Parsing Service - Stopping...")
|
||||
LOG.info("Vitrage SNMP Parsing Service - Stopped!")
|
||||
|
||||
|
@ -20,7 +20,8 @@ import uuid
|
||||
|
||||
|
||||
from keystonemiddleware import fixture as ksm_fixture
|
||||
from mock import mock
|
||||
# noinspection PyPackageRequirements
|
||||
import mock
|
||||
from vitrage.tests.functional.api.v1 import FunctionalTest
|
||||
|
||||
EVENT_DETAILS = {
|
||||
|
@ -18,7 +18,8 @@
|
||||
import uuid
|
||||
|
||||
from datetime import datetime
|
||||
from mock import mock
|
||||
# noinspection PyPackageRequirements
|
||||
import mock
|
||||
from six.moves import http_client as httplib
|
||||
from vitrage.tests.functional.api.v1 import FunctionalTest
|
||||
|
||||
|
@ -14,9 +14,9 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
# noinspection PyPackageRequirements
|
||||
from datetime import datetime
|
||||
from mock import mock
|
||||
# noinspection PyPackageRequirements
|
||||
import mock
|
||||
import requests_mock
|
||||
from vitrage.middleware.keycloak import KeycloakAuth
|
||||
from vitrage.tests.functional.api.v1 import FunctionalTest
|
||||
|
@ -17,7 +17,7 @@
|
||||
|
||||
from datetime import datetime
|
||||
# noinspection PyPackageRequirements
|
||||
from mock import mock
|
||||
import mock
|
||||
from vitrage.common.utils import compress_obj
|
||||
|
||||
from vitrage.storage.sqlalchemy import models
|
||||
|
84
vitrage/tests/functional/api/v1/test_service.py
Normal file
84
vitrage/tests/functional/api/v1/test_service.py
Normal 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))
|
@ -12,8 +12,10 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from mock import mock
|
||||
# noinspection PyPackageRequirements
|
||||
import mock
|
||||
from oslo_config import cfg
|
||||
# noinspection PyPackageRequirements
|
||||
from testtools import matchers
|
||||
|
||||
from vitrage.common.constants import DatasourceOpts as DSOpts
|
||||
@ -61,12 +63,12 @@ class PrometheusDriverTest(base.BaseTest):
|
||||
# Test Action
|
||||
observed_valid_ip = driver._adjust_label_value(valid_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
|
||||
self.assertEqual(hostname, observed_valid_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')
|
||||
def test_calculate_host_vitrage_entity_unique_props(self, mock_socket):
|
||||
|
@ -124,6 +124,9 @@ class TestSnmpParsing(base.BaseTest):
|
||||
super(TestSnmpParsing, cls).setUpClass()
|
||||
cls.conf = cfg.ConfigOpts()
|
||||
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):
|
||||
parsing_service = SnmpParsingService(1, self.conf)
|
||||
|
Loading…
Reference in New Issue
Block a user