Fix masakari-monitor connection issue

From openstacksdk version 0.11.1 onwards, there is no way
you can add service to the connection. Hence we need to monkey
_find_service_filter_class method from sdk to allow
to point to thee correct service filter class from masakariclient.

Solution provided here is not going to work in sdk 0.9.19 and
0.10.0 versions. Because service descriptor is not supported
in 0.9.19 and 0.10.0 sdk versions and profile parameter doesn't
work correctly during connection initialization.

Also solution provided here is not going to work in sdk 0.13.0
and above, it will still fails with below error
"'Connection' object has no attribute 'ha'"
this is because 'Connection' object expecting instance_ha not ha

Co-Author: tpatil <tushar.vitthal.patil@gmail.com>
Change-Id: Ic10d1b380abc8b3b6cc38015c9c3bf4ab7705a0c
Depends-On: I24ec493f3333a5b3f6bfed9395c921fb28500065
Closes-Bug: #1779752
This commit is contained in:
shilpa.devharakar 2018-08-03 10:16:23 +05:30
parent da40a2f881
commit 3ffd576b79
9 changed files with 168 additions and 107 deletions

View File

@ -3,3 +3,12 @@
- check-requirements
- openstack-python-jobs
- openstack-python35-jobs-nonvoting
check:
jobs:
- requirements-check:
voting: false
gate:
jobs:
- requirements-check:
voting: false

View File

@ -0,0 +1,47 @@
# Copyright 2018 NTT DATA.
# All Rights Reserved.
#
# 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 importlib
import warnings
def masakari_service_filter_class(service_type):
package_name = 'openstack.{service_type}'.format(
service_type=service_type).replace('-', '_')
module_name = service_type.replace('-', '_') + '_service'
class_name = ''.join(
[part.capitalize() for part in module_name.split('_')])
try:
import_name = '.'.join([package_name, module_name])
if (class_name == "InstanceHaService" and
import_name == "openstack.instance_ha."
"instance_ha_service"):
class_name = "HAService"
import_name = "masakariclient.sdk.ha.ha_service"
service_filter_module = importlib.import_module(import_name)
except ImportError as e:
# ImportWarning is ignored by default. This warning is here
# as an opt-in for people trying to figure out why something
# didn't work.
warnings.warn(
"Could not import {service_type} service filter: {e}".format(
service_type=service_type, e=str(e)),
ImportWarning)
return None
# There are no cases in which we should have a module but not the class
# inside it.
service_filter_class = getattr(service_filter_module, class_name)
return service_filter_class

View File

@ -14,6 +14,7 @@
"""Starter script for Masakari Host Monitor."""
import pbr.version
import sys
from oslo_log import log as logging
@ -32,6 +33,21 @@ def main():
logging.setup(CONF, "masakarimonitors")
utils.monkey_patch()
# From openstacksdk version 0.11.1 onwards, there is no way
# you can add service to the connection. Hence we need to monkey patch
# _find_service_filter_class method from sdk to allow
# to point to the correct service filter class implemented in
# masakari-monitors.
sdk_ver = pbr.version.VersionInfo('openstacksdk').version_string()
if sdk_ver in ['0.11.1', '0.11.2', '0.11.3']:
utils.monkey_patch_for_openstacksdk("openstack._meta:masakarimonitors."
"cmd."
"masakari_service_filter_class")
if sdk_ver in ['0.12.0']:
utils.monkey_patch_for_openstacksdk("openstack._meta.connection:"
"masakarimonitors.cmd."
"masakari_service_filter_class")
server = service.Service.create(binary='masakarimonitors-hostmonitor')
service.serve(server)
service.wait()

View File

@ -14,6 +14,7 @@
"""Starter script for Masakari Instance Monitor."""
import pbr.version
import sys
from oslo_log import log as logging
@ -32,6 +33,21 @@ def main():
logging.setup(CONF, "masakarimonitors")
utils.monkey_patch()
# From openstacksdk version 0.11.1 onwards, there is no way
# you can add service to the connection. Hence we need to monkey patch
# _find_service_filter_class method from sdk to allow
# to point to the correct service filter class implemented in
# masakari-monitors.
sdk_ver = pbr.version.VersionInfo('openstacksdk').version_string()
if sdk_ver in ['0.11.1', '0.11.2', '0.11.3']:
utils.monkey_patch_for_openstacksdk("openstack._meta:masakarimonitors."
"cmd."
"masakari_service_filter_class")
if sdk_ver in ['0.12.0']:
utils.monkey_patch_for_openstacksdk("openstack._meta.connection:"
"masakarimonitors.cmd."
"masakari_service_filter_class")
server = service.Service.create(binary='masakarimonitors-instancemonitor')
service.serve(server)
service.wait()

View File

@ -14,6 +14,7 @@
"""Starter script for Masakari Process Monitor."""
import pbr.version
import sys
from oslo_log import log as logging
@ -32,6 +33,21 @@ def main():
logging.setup(CONF, "masakarimonitors")
utils.monkey_patch()
# From openstacksdk version 0.11.1 onwards, there is no way
# you can add service to the connection. Hence we need to monkey patch
# _find_service_filter_class method from sdk to allow
# to point to the correct service filter class implemented in
# masakari-monitors.
sdk_ver = pbr.version.VersionInfo('openstacksdk').version_string()
if sdk_ver in ['0.11.1', '0.11.2', '0.11.3']:
utils.monkey_patch_for_openstacksdk("openstack._meta:masakarimonitors."
"cmd."
"masakari_service_filter_class")
if sdk_ver in ['0.12.0']:
utils.monkey_patch_for_openstacksdk("openstack._meta.connection:"
"masakarimonitors.cmd."
"masakari_service_filter_class")
server = service.Service.create(binary='masakarimonitors-processmonitor')
service.serve(server)
service.wait()

View File

@ -13,12 +13,18 @@
# limitations under the License.
import eventlet
import pbr.version
from keystoneauth1.identity.generic import password as ks_password
from keystoneauth1 import session as ks_session
from openstack import connection
sdk_ver = pbr.version.VersionInfo('openstacksdk').version_string()
if sdk_ver in ['0.11.0']:
from masakariclient.sdk.ha.v1 import _proxy
from openstack import service_description
from openstack import exceptions
from openstack import profile
from oslo_log import log as oslo_logging
from masakariclient.sdk.ha import ha_service
import masakarimonitors.conf
LOG = oslo_logging.getLogger(__name__)
@ -34,23 +40,25 @@ class SendNotification(object):
project_name, username, password, project_domain_id,
user_domain_id):
# Create profile object.
prof = profile.Profile()
prof._add_service(ha_service.HAService(version=api_version))
prof.set_name(PROFILE_TYPE, PROFILE_NAME)
prof.set_region(PROFILE_TYPE, region)
prof.set_version(PROFILE_TYPE, api_version)
prof.set_interface(PROFILE_TYPE, interface)
# Get connection.
conn = connection.Connection(
auth = ks_password.Password(
auth_url=auth_url,
project_name=project_name,
username=username,
password=password,
project_domain_id=project_domain_id,
user_domain_id=user_domain_id,
profile=prof)
project_name=project_name,
project_domain_id=project_domain_id)
session = ks_session.Session(auth=auth)
# Get connection.
if sdk_ver >= '0.11.1':
conn = connection.Connection(session=session, interface=interface,
region_name=region)
elif sdk_ver in ['0.11.0']:
desc = service_description.ServiceDescription(service_type='ha',
proxy_class=_proxy.Proxy)
conn = connection.Connection(session=session, interface=interface,
region_name=region)
conn.add_service(desc)
return conn

View File

@ -12,6 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
from keystoneauth1 import session
import mock
import testtools
import uuid
@ -19,10 +20,8 @@ import uuid
import eventlet
from openstack import connection
from openstack import exceptions
from openstack import profile
from oslo_utils import timeutils
from masakariclient.sdk.ha import ha_service
from masakarimonitors.ha import masakari
from masakarimonitors.objects import event_constants as ec
@ -60,55 +59,31 @@ class TestSendNotification(testtools.TestCase):
}
@mock.patch.object(connection, 'Connection')
@mock.patch.object(profile, 'Profile')
def test_send_notification(self,
mock_Profile,
mock_Connection):
mock_prof = mock.Mock()
mock_Profile.return_value = mock_prof
mock_conn = mock.Mock()
mock_Connection.return_value = mock_conn
mock_connection):
mock_session = mock.Mock(spec=session.Session)
mock_session.auth = mock.Mock()
mock_session.auth.auth_url = 'https://auth.example.com'
mock_connection = connection.Connection(session=mock_session)
notifier = masakari.SendNotification()
notifier.send_notification(
self.api_retry_max, self.api_retry_interval, self.event)
mock_prof._add_service.assert_called_once_with(
ha_service.HAService(version='v1'))
mock_prof.set_name.assert_called_once_with(
PROFILE_TYPE, PROFILE_NAME)
mock_prof.set_region.assert_called_once_with(
PROFILE_TYPE, 'RegionOne')
mock_prof.set_version.assert_called_once_with(
PROFILE_TYPE, 'v1')
mock_prof.set_interface.assert_called_once_with(
PROFILE_TYPE, 'public')
mock_Connection.assert_called_once_with(
auth_url=None,
project_name=None,
username=None,
password=None,
project_domain_id=None,
user_domain_id=None,
profile=mock_prof)
mock_conn.ha.create_notification.assert_called_once_with(
mock_connection.ha.create_notification.assert_called_once_with(
type=self.event['notification']['type'],
hostname=self.event['notification']['hostname'],
generated_time=self.event['notification']['generated_time'],
payload=self.event['notification']['payload'])
@mock.patch.object(connection, 'Connection')
@mock.patch.object(profile, 'Profile')
def test_send_notification_409_error(self,
mock_Profile,
mock_Connection):
mock_connection):
mock_prof = mock.Mock()
mock_Profile.return_value = mock_prof
mock_conn = mock.Mock()
mock_Connection.return_value = mock_conn
mock_session = mock.Mock(spec=session.Session)
mock_session.auth = mock.Mock()
mock_session.auth.auth_url = 'https://auth.example.com'
mock_connection = connection.Connection(session=mock_session)
# TODO(samP): Remove attribute check and else case if
# openstacksdk is bumped up from '>=0.9.19' to '>=0.10.0'
@ -119,31 +94,12 @@ class TestSendNotification(testtools.TestCase):
else:
status_ex = exceptions.HttpException(http_status=409)
mock_conn.ha.create_notification.side_effect = status_ex
mock_connection.ha.create_notification.side_effect = status_ex
notifier = masakari.SendNotification()
notifier.send_notification(
self.api_retry_max, self.api_retry_interval, self.event)
mock_prof._add_service.assert_called_once_with(
ha_service.HAService(version='v1'))
mock_prof.set_name.assert_called_once_with(
PROFILE_TYPE, PROFILE_NAME)
mock_prof.set_region.assert_called_once_with(
PROFILE_TYPE, 'RegionOne')
mock_prof.set_version.assert_called_once_with(
PROFILE_TYPE, 'v1')
mock_prof.set_interface.assert_called_once_with(
PROFILE_TYPE, 'public')
mock_Connection.assert_called_once_with(
auth_url=None,
project_name=None,
username=None,
password=None,
project_domain_id=None,
user_domain_id=None,
profile=mock_prof)
mock_conn.ha.create_notification.assert_called_once_with(
mock_connection.ha.create_notification.assert_called_once_with(
type=self.event['notification']['type'],
hostname=self.event['notification']['hostname'],
generated_time=self.event['notification']['generated_time'],
@ -151,16 +107,14 @@ class TestSendNotification(testtools.TestCase):
@mock.patch.object(eventlet.greenthread, 'sleep')
@mock.patch.object(connection, 'Connection')
@mock.patch.object(profile, 'Profile')
def test_send_notification_500_error(self,
mock_Profile,
mock_Connection,
mock_connection,
mock_sleep):
mock_prof = mock.Mock()
mock_Profile.return_value = mock_prof
mock_conn = mock.Mock()
mock_Connection.return_value = mock_conn
mock_session = mock.Mock(spec=session.Session)
mock_session.auth = mock.Mock()
mock_session.auth.auth_url = 'https://auth.example.com'
mock_connection = connection.Connection(session=mock_session)
# TODO(samP): Remove attribute check and else case if
# openstacksdk is bumped up from '>=0.9.19' to '>=0.10.0'
@ -171,36 +125,17 @@ class TestSendNotification(testtools.TestCase):
else:
status_ex = exceptions.HttpException(http_status=500)
mock_conn.ha.create_notification.side_effect = status_ex
mock_connection.ha.create_notification.side_effect = status_ex
mock_sleep.return_value = None
notifier = masakari.SendNotification()
notifier.send_notification(
self.api_retry_max, self.api_retry_interval, self.event)
mock_prof._add_service.assert_called_once_with(
ha_service.HAService(version='v1'))
mock_prof.set_name.assert_called_once_with(
PROFILE_TYPE, PROFILE_NAME)
mock_prof.set_region.assert_called_once_with(
PROFILE_TYPE, 'RegionOne')
mock_prof.set_version.assert_called_once_with(
PROFILE_TYPE, 'v1')
mock_prof.set_interface.assert_called_once_with(
PROFILE_TYPE, 'public')
mock_Connection.assert_called_once_with(
auth_url=None,
project_name=None,
username=None,
password=None,
project_domain_id=None,
user_domain_id=None,
profile=mock_prof)
mock_conn.ha.create_notification.assert_called_with(
mock_connection.ha.create_notification.assert_called_with(
type=self.event['notification']['type'],
hostname=self.event['notification']['hostname'],
generated_time=self.event['notification']['generated_time'],
payload=self.event['notification']['payload'])
self.assertEqual(self.api_retry_max + 1,
mock_conn.ha.create_notification.call_count)
mock_connection.ha.create_notification.call_count)

View File

@ -16,15 +16,14 @@
import contextlib
import inspect
import pyclbr
import shutil
import sys
import tempfile
from oslo_concurrency import processutils
from oslo_log import log as logging
from oslo_utils import importutils
import pyclbr
import shutil
import six
import sys
import tempfile
import masakarimonitors.conf
from masakarimonitors.i18n import _
@ -36,6 +35,20 @@ CONF = masakarimonitors.conf.CONF
LOG = logging.getLogger(__name__)
def monkey_patch_for_openstacksdk(module_and_decorator_name):
module, decorator_name = module_and_decorator_name.split(':')
# import decorator function
decorator = importutils.import_class(decorator_name)
__import__(module)
# Retrieve module information using pyclbr
module_data = pyclbr.readmodule_ex(module)
for key in module_data.keys():
# set the decorator for the function
if isinstance(module_data[key], pyclbr.Function):
if key == "_find_service_filter_class":
setattr(sys.modules[module], key, decorator)
def monkey_patch():
"""monkey_patch function.

View File

@ -3,6 +3,7 @@
# process, which may cause wedges in the gate later.
libvirt-python>=1.2.5 # LGPLv2+
openstacksdk>=0.11.0 # Apache-2.0
oslo.concurrency>=3.8.0 # Apache-2.0
oslo.config!=4.3.0,!=4.4.0,>=4.0.0 # Apache-2.0
oslo.i18n!=3.15.2,>=2.1.0 # Apache-2.0