From f785d6a413b32a369f67a4adbce6a802c03b0051 Mon Sep 17 00:00:00 2001 From: Saad Zaher Date: Tue, 22 Dec 2015 16:24:20 +0000 Subject: [PATCH] Adding support for plugable monitoring systems adding support for pbr restructuring osha Change-Id: I0a899deeb33a5ebe77a199d33f3c0af48cddcbe6 --- .gitignore | 4 + AUTHORS | 0 ChangeLog | 0 etc/osha.conf | 181 +++++++++++++++++++++++++ evacuate.py | 22 --- osha/__init__.py | 20 +++ osha/common/__init__.py | 0 osha/common/config.py | 167 +++++++++++++++++++++++ daemon.py => osha/common/daemon.py | 0 osclient.py => osha/common/osclient.py | 20 ++- osha/common/utils.py | 23 ++++ osha/evacuate.py | 34 +++++ ipmitool.py => osha/ipmitool.py | 0 osha/main.py | 36 +++++ monitor.py => osha/monitor.py | 14 +- osha/monitors/__init__.py | 13 ++ osha/monitors/common/__init__.py | 1 + osha/monitors/common/driver.py | 96 +++++++++++++ osha/monitors/common/manager.py | 66 +++++++++ osha/monitors/drivers/__init__.py | 1 + osha/monitors/drivers/osha/__init__.py | 1 + osha/monitors/drivers/osha/driver.py | 131 ++++++++++++++++++ osha.py => osha/oshad.py | 6 +- servers.yml => osha/servers.yml | 0 yaml_parser.py => osha/yaml_parser.py | 0 requirements.txt | 3 +- setup.cfg | 37 +++++ setup.py | 29 ++++ 28 files changed, 877 insertions(+), 28 deletions(-) create mode 100644 .gitignore create mode 100644 AUTHORS create mode 100644 ChangeLog create mode 100644 etc/osha.conf delete mode 100644 evacuate.py create mode 100644 osha/__init__.py create mode 100644 osha/common/__init__.py create mode 100644 osha/common/config.py rename daemon.py => osha/common/daemon.py (100%) rename osclient.py => osha/common/osclient.py (85%) create mode 100644 osha/common/utils.py create mode 100644 osha/evacuate.py rename ipmitool.py => osha/ipmitool.py (100%) create mode 100644 osha/main.py rename monitor.py => osha/monitor.py (82%) create mode 100644 osha/monitors/__init__.py create mode 100644 osha/monitors/common/__init__.py create mode 100644 osha/monitors/common/driver.py create mode 100644 osha/monitors/common/manager.py create mode 100644 osha/monitors/drivers/__init__.py create mode 100644 osha/monitors/drivers/osha/__init__.py create mode 100644 osha/monitors/drivers/osha/driver.py rename osha.py => osha/oshad.py (95%) rename servers.yml => osha/servers.yml (100%) rename yaml_parser.py => osha/yaml_parser.py (100%) create mode 100644 setup.cfg create mode 100644 setup.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9fbf606 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +*.pyc +*.egg/ +*.egg-info/ +build/ diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..e69de29 diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..e69de29 diff --git a/etc/osha.conf b/etc/osha.conf new file mode 100644 index 0000000..f3a2f1e --- /dev/null +++ b/etc/osha.conf @@ -0,0 +1,181 @@ +[DEFAULT] + +# +# From osha +# + +# Time to wait between different operations (integer value) +#wait = 30 + +# +# From oslo.log +# + +# Print debugging output (set logging level to DEBUG instead of +# default WARNING level). (boolean value) +#debug = false + +# Print more verbose output (set logging level to INFO instead of +# default WARNING level). (boolean value) +#verbose = false + +# The name of a logging configuration file. This file is appended to +# any existing logging configuration files. For details about logging +# configuration files, see the Python logging module documentation. +# (string value) +# Deprecated group/name - [DEFAULT]/log_config +#log_config_append = + +# DEPRECATED. A logging.Formatter log message format string which may +# use any of the available logging.LogRecord attributes. This option +# is deprecated. Please use logging_context_format_string and +# logging_default_format_string instead. (string value) +#log_format = + +# Format string for %%(asctime)s in log records. Default: %(default)s +# . (string value) +#log_date_format = %Y-%m-%d %H:%M:%S + +# (Optional) Name of log file to output to. If no default is set, +# logging will go to stdout. (string value) +# Deprecated group/name - [DEFAULT]/logfile +#log_file = + +# (Optional) The base directory used for relative --log-file paths. +# (string value) +# Deprecated group/name - [DEFAULT]/logdir +#log_dir = + +# Use syslog for logging. Existing syslog format is DEPRECATED during +# I, and will change in J to honor RFC5424. (boolean value) +#use_syslog = false + +# (Optional) Enables or disables syslog rfc5424 format for logging. If +# enabled, prefixes the MSG part of the syslog message with APP-NAME +# (RFC5424). The format without the APP-NAME is deprecated in I, and +# will be removed in J. (boolean value) +#use_syslog_rfc_format = false + +# Syslog facility to receive log lines. (string value) +#syslog_log_facility = LOG_USER + +# Log output to standard error. (boolean value) +#use_stderr = true + +# Format string to use for log messages with context. (string value) +#logging_context_format_string = %(asctime)s.%(msecs)03d %(process)d %(levelname)s %(name)s [%(request_id)s %(user_identity)s] %(instance)s%(message)s + +# Format string to use for log messages without context. (string +# value) +#logging_default_format_string = %(asctime)s.%(msecs)03d %(process)d %(levelname)s %(name)s [-] %(instance)s%(message)s + +# Data to append to log format when level is DEBUG. (string value) +#logging_debug_format_suffix = %(funcName)s %(pathname)s:%(lineno)d + +# Prefix each line of exception output with this format. (string +# value) +#logging_exception_prefix = %(asctime)s.%(msecs)03d %(process)d TRACE %(name)s %(instance)s + +# List of logger=LEVEL pairs. (list value) +#default_log_levels = amqp=WARN,amqplib=WARN,boto=WARN,qpid=WARN,sqlalchemy=WARN,suds=INFO,oslo.messaging=INFO,iso8601=WARN,requests.packages.urllib3.connectionpool=WARN,urllib3.connectionpool=WARN,websocket=WARN,requests.packages.urllib3.util.retry=WARN,urllib3.util.retry=WARN,keystonemiddleware=WARN,routes.middleware=WARN,stevedore=WARN + +# Enables or disables publication of error events. (boolean value) +#publish_errors = false + +# Enables or disables fatal status of deprecations. (boolean value) +#fatal_deprecations = false + +# The format for an instance that is passed with the log message. +# (string value) +#instance_format = "[instance: %(uuid)s] " + +# The format for an instance UUID that is passed with the log message. +# (string value) +#instance_uuid_format = "[instance: %(uuid)s] " + + +[keystone] + +# +# From osha +# + +# Name used for authentication with the OpenStack Identity service. +# Defaults to env[OS_USERNAME]. (string value) +#os_username = + +# Password used for authentication with the OpenStack Identity +# service. Defaults to env[OS_PASSWORD]. (string value) +#os_password = + +# Project name to scope to. Defaults to env[OS_PROJECT_NAME]. (string +# value) +#os_project_name = + +# Domain name containing project. Defaults to +# env[OS_PROJECT_DOMAIN_NAME]. (string value) +#os_project_domain_name = + +# User's domain name. Defaults to env[OS_USER_DOMAIN_NAME]. (string +# value) +#os_user_domain_name = + +# Tenant to request authorization on. Defaults to env[OS_TENANT_NAME]. +# (string value) +#os_tenant_name = + +# Tenant to request authorization on. Defaults to env[OS_TENANT_ID]. +# (string value) +#os_tenant_id = + +# Specify the Identity endpoint to use for authentication. Defaults to +# env[OS_AUTH_URL]. (string value) +#os_auth_url = + +# Specify the Freezer backup service endpoint to use. Defaults to +# env[OS_BACKUP_URL]. (string value) +#os_backup_url = + +# Specify the region to use. Defaults to env[OS_REGION_NAME]. (string +# value) +#os_region_name = + +# Specify an existing token to use instead of retrieving one via +# authentication (e.g. with username & password). Defaults to +# env[OS_TOKEN]. (string value) +#os_token = + +# Identity API version: 2.0 or 3. Defaults to +# env[OS_IDENTITY_API_VERSION] (string value) +#os_identity_api_version = + +# Endpoint type to select. Valid endpoint types: "public" or +# "publicURL", "internal" or "internalURL", "admin" or "adminURL". +# Defaults to env[OS_ENDPOINT_TYPE] or "public" (string value) +# Allowed values: public, publicURL, internal, internalURL, admin, adminURL +#os_endpoint_type = public + + +[monitoring] + +# +# From osha +# + +# Driver used to get a status updates of compute nodes (string value) +#driver = osha.monitors.plugins.osha.OshaDriver + +# username to be used to initialize the monitoring driver (string +# value) +#username = + +# Password to be used for initializing monitoring driver (string +# value) +#password = + +# Monitoring system API endpoint (string value) +#endpoint = + +# List of kwargs if you want to pass it to initialize the monitoring +# driver (dict value) +#kwargs = diff --git a/evacuate.py b/evacuate.py deleted file mode 100644 index 70edc9f..0000000 --- a/evacuate.py +++ /dev/null @@ -1,22 +0,0 @@ -#__author__ = 'saad' -from monitor import Monitor -import osclient - -password = 'BOMrLNGHsoBb' -user_id = 'ec2548d6acb54e7ba24f479e2f3cb1a5' -username = 'admin' -auth_url = 'http://192.168.245.9:35357/v3' -project_name = 'demo' -project_id = 'f749b2874b0040aca92ea131210eb774' -user_domain_name = 'Default' -project_domain_name = 'Default' - -client = osclient.OSClient(authurl=auth_url, - username=username, - password=password, - user_domain_name=user_domain_name, - project_name=project_name, - project_domain_name=project_domain_name, - endpoint_type='internal') -monitor = Monitor(client, 1) -monitor.monitor() \ No newline at end of file diff --git a/osha/__init__.py b/osha/__init__.py new file mode 100644 index 0000000..afa3e70 --- /dev/null +++ b/osha/__init__.py @@ -0,0 +1,20 @@ +# (c) Copyright 2014,2015 Hewlett-Packard Development Company, L.P. +# +# 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. + +# Osha Versions + +import pbr.version + + +__version__ = pbr.version.VersionInfo('osha').version_string() \ No newline at end of file diff --git a/osha/common/__init__.py b/osha/common/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/osha/common/config.py b/osha/common/config.py new file mode 100644 index 0000000..5f59212 --- /dev/null +++ b/osha/common/config.py @@ -0,0 +1,167 @@ +# (c) Copyright 2014,2015 Hewlett-Packard Development Company, L.P. +# +# 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 +from osha.common.utils import env +import sys +from osha import __version__ as OSHA_VERSION +from oslo_log import log + +CONF = cfg.CONF + +_MONITORS = [ + cfg.StrOpt('driver', + default='osha.monitors.plugins.osha.OshaDriver', + help='Driver used to get a status updates of compute nodes'), + cfg.StrOpt('username', + help='username to be used to initialize the monitoring driver'), + cfg.StrOpt('password', + help='Password to be used for initializing monitoring driver'), + cfg.StrOpt('endpoint', + help='Monitoring system API endpoint'), + cfg.DictOpt('kwargs', + default={}, + help='List of kwargs if you want to pass it to initialize' + ' the monitoring driver') +] + + +_COMMON = [ + cfg.IntOpt('wait', + default=30, + help='Time to wait between different operations') +] + + +def build_os_options(): + osclient_opts = [ + cfg.StrOpt('os-username', + default=env('OS_USERNAME'), + help='Name used for authentication with the OpenStack ' + 'Identity service. Defaults to env[OS_USERNAME].', + dest='os_username'), + cfg.StrOpt('os-password', + default=env('OS_PASSWORD'), + help='Password used for authentication with the OpenStack ' + 'Identity service. Defaults to env[OS_PASSWORD].', + dest='os_password'), + cfg.StrOpt('os-project-name', + default=env('OS_PROJECT_NAME'), + help='Project name to scope to. Defaults to ' + 'env[OS_PROJECT_NAME].', + dest='os_project_name'), + cfg.StrOpt('os-project-domain-name', + default=env('OS_PROJECT_DOMAIN_NAME'), + help='Domain name containing project. Defaults to ' + 'env[OS_PROJECT_DOMAIN_NAME].', + dest='os_project_domain_name'), + cfg.StrOpt('os-user-domain-name', + default=env('OS_USER_DOMAIN_NAME'), + help='User\'s domain name. Defaults to ' + 'env[OS_USER_DOMAIN_NAME].', + dest='os_user_domain_name'), + cfg.StrOpt('os-tenant-name', + default=env('OS_TENANT_NAME'), + help='Tenant to request authorization on. Defaults to ' + 'env[OS_TENANT_NAME].', + dest='os_tenant_name'), + cfg.StrOpt('os-tenant-id', + default=env('OS_TENANT_ID'), + help='Tenant to request authorization on. Defaults to ' + 'env[OS_TENANT_ID].', + dest='os_tenant_id'), + cfg.StrOpt('os-auth-url', + default=env('OS_AUTH_URL'), + help='Specify the Identity endpoint to use for ' + 'authentication. Defaults to env[OS_AUTH_URL].', + dest='os_auth_url'), + cfg.StrOpt('os-backup-url', + default=env('OS_BACKUP_URL'), + help='Specify the Freezer backup service endpoint to use. ' + 'Defaults to env[OS_BACKUP_URL].', + dest='os_backup_url'), + cfg.StrOpt('os-region-name', + default=env('OS_REGION_NAME'), + help='Specify the region to use. Defaults to ' + 'env[OS_REGION_NAME].', + dest='os_region_name'), + cfg.StrOpt('os-token', + default=env('OS_TOKEN'), + help='Specify an existing token to use instead of retrieving' + ' one via authentication (e.g. with username & ' + 'password). Defaults to env[OS_TOKEN].', + dest='os_token'), + cfg.StrOpt('os-identity-api-version', + default=env('OS_IDENTITY_API_VERSION'), + help='Identity API version: 2.0 or 3. ' + 'Defaults to env[OS_IDENTITY_API_VERSION]', + dest='os_identity_api_version'), + cfg.StrOpt('os-endpoint-type', + choices=['public', 'publicURL', 'internal', 'internalURL', + 'admin', 'adminURL'], + default=env('OS_ENDPOINT_TYPE') or 'public', + help='Endpoint type to select. Valid endpoint types: ' + '"public" or "publicURL", "internal" or "internalURL",' + ' "admin" or "adminURL". Defaults to ' + 'env[OS_ENDPOINT_TYPE] or "public"', + dest='os_endpoint_type'), + ] + + return osclient_opts + + +def configure(): + CONF.register_cli_opts(build_os_options()) + CONF.register_opts(_COMMON) + monitors_grp = cfg.OptGroup('monitoring', + title='Monitoring', + help='Monitoring Driver/plugin to be used to ' + 'monitor compute nodes') + CONF.register_group(monitors_grp) + CONF.register_opts(_MONITORS, group='monitoring') + + default_conf = cfg.find_config_files('osha', 'osha', + '.conf') + log.register_options(CONF) + + CONF(args=sys.argv[1:], + project='osha', + default_config_files=default_conf, + version=OSHA_VERSION + ) + + +def setup_logging(): + _DEFAULT_LOG_LEVELS = ['amqp=WARN', 'amqplib=WARN', 'boto=WARN','qpid=WARN', + 'stevedore=WARN', 'oslo_log=INFO', 'iso8601=WARN', + 'requests.packages.urllib3.connectionpool=WARN', + 'urllib3.connectionpool=WARN', 'websocket=WARN', + 'keystonemiddleware=WARN', 'osha=INFO'] + + _DEFAULT_LOGGING_CONTEXT_FORMAT = ('%(asctime)s.%(msecs)03d %(process)d ' + '%(levelname)s %(name)s [%(request_id)s ' + '%(user_identity)s] %(instance)s' + '%(message)s') + log.set_defaults(_DEFAULT_LOGGING_CONTEXT_FORMAT, _DEFAULT_LOG_LEVELS) + log.setup(CONF, 'osha', version=OSHA_VERSION) + + +def list_opts(): + _OPTS = { + None: _COMMON, + 'monitoring': _MONITORS, + 'keystone': build_os_options() + } + + return _OPTS.items() + diff --git a/daemon.py b/osha/common/daemon.py similarity index 100% rename from daemon.py rename to osha/common/daemon.py diff --git a/osclient.py b/osha/common/osclient.py similarity index 85% rename from osclient.py rename to osha/common/osclient.py index ed92810..d4bba81 100644 --- a/osclient.py +++ b/osha/common/osclient.py @@ -1,4 +1,16 @@ -__author__ = 'saad' +# (c) Copyright 2014,2015 Hewlett-Packard Development Company, L.P. +# +# 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 keystoneclient.auth.identity import v3 from keystoneclient import session @@ -7,7 +19,7 @@ from neutronclient.v2_0 import client as neutronclient class OSClient: - def __init__(self, authurl, authmethod = 'password', ** kwargs): + def __init__(self, authurl, authmethod='password', ** kwargs): """ Provide Openstack credentials to initalize the connection to Openstack :param authmethod: string authmethod should be password or token but @@ -114,3 +126,7 @@ class OSClient: except Exception as e: print "ERROR ::: ", e return output + + def get_session(self): + auth_session = session.Session(auth=self.authSession.auth) + return auth_session diff --git a/osha/common/utils.py b/osha/common/utils.py new file mode 100644 index 0000000..c660b70 --- /dev/null +++ b/osha/common/utils.py @@ -0,0 +1,23 @@ +# (c) Copyright 2014,2015 Hewlett-Packard Development Company, L.P. +# +# 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 os + + +def env(*env_vars, **kwargs): + for variable in env_vars: + value = os.environ.get(variable, None) + if value: + return value + return kwargs.get('default', '') diff --git a/osha/evacuate.py b/osha/evacuate.py new file mode 100644 index 0000000..f9b3756 --- /dev/null +++ b/osha/evacuate.py @@ -0,0 +1,34 @@ +# (c) Copyright 2014,2015 Hewlett-Packard Development Company, L.P. +# +# 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 osha.monitor import Monitor +import osclient + +password = 'a22dQNcT' +user_id = None +username = 'admin' +auth_url = 'http://192.168.245.9:35357/v3' +project_name = 'demo' +project_id = None +user_domain_name = 'Default' +project_domain_name = 'Default' + +client = osclient.OSClient(authurl=auth_url, + username=username, + password=password, + user_domain_name=user_domain_name, + project_name=project_name, + project_domain_name=project_domain_name, + endpoint_type='internal') +monitor = Monitor(client, 1) +monitor.monitor() \ No newline at end of file diff --git a/ipmitool.py b/osha/ipmitool.py similarity index 100% rename from ipmitool.py rename to osha/ipmitool.py diff --git a/osha/main.py b/osha/main.py new file mode 100644 index 0000000..c5dd313 --- /dev/null +++ b/osha/main.py @@ -0,0 +1,36 @@ +# (c) Copyright 2014,2015 Hewlett-Packard Development Company, L.P. +# +# 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 osha.common import config +from oslo_config import cfg +from oslo_log import log +from oslo_utils import importutils +from osha.monitors.common.manager import MonitorManager + +CONF = cfg.CONF +LOG = log.getLogger(__name__) + + +def main(): + config.configure() + config.setup_logging() + LOG.info('Starting osha ... ') + # load and initialize the monitoring driver + monitor = MonitorManager() + # Do the monitoring procedure + # Monitor, analyse, nodes down ?, wait, double check ? evacuate .. + nodes = monitor.monitor() + print "Evacuate those nodes:> ", nodes + if nodes: + # evacuate process goes here ! + pass diff --git a/monitor.py b/osha/monitor.py similarity index 82% rename from monitor.py rename to osha/monitor.py index 7346ba4..f086903 100644 --- a/monitor.py +++ b/osha/monitor.py @@ -1,4 +1,16 @@ -#__author__ = 'saad' +# (c) Copyright 2014,2015 Hewlett-Packard Development Company, L.P. +# +# 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 time import sleep diff --git a/osha/monitors/__init__.py b/osha/monitors/__init__.py new file mode 100644 index 0000000..f7f293a --- /dev/null +++ b/osha/monitors/__init__.py @@ -0,0 +1,13 @@ +# (c) Copyright 2014,2015 Hewlett-Packard Development Company, L.P. +# +# 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. diff --git a/osha/monitors/common/__init__.py b/osha/monitors/common/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/osha/monitors/common/__init__.py @@ -0,0 +1 @@ + diff --git a/osha/monitors/common/driver.py b/osha/monitors/common/driver.py new file mode 100644 index 0000000..33ea93b --- /dev/null +++ b/osha/monitors/common/driver.py @@ -0,0 +1,96 @@ +# (c) Copyright 2014,2015 Hewlett-Packard Development Company, L.P. +# +# 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 abc +import six + + +@six.add_metaclass(abc.ABCMeta) +class MonitorBaseDriver(object): + """ + Abstract class that all monitoring plugins should implement to have a + unified interface and as many plugins as we want... + """ + + def __init__(self, username, password, endpoint, kwargs): + """ + Initializing the driver. Any monitoring system requires the following + parameters to call it's api. All these parameters can be passed from the + configuration file in /etc/osha/osha.conf + :param username: Username + :param password: Password + :param endpoint: API URL + :param kwargs: any additional parameters can be passed using this config + option. + """ + self.username = username + self.password = password + self.endpoint = endpoint + self.kwargs = kwargs + + @abc.abstractmethod + def get_data(self): + """ + Gathering metrics data. making the actual api call to + the monitoring system and get a list of nodes status. + """ + pass + + @abc.abstractmethod + def get_metrics(self): + """ + return list of metrics used to monitor compute nodes. it's Optional + not all drivers need to implement this method. + """ + raise NotImplementedError() + + @abc.abstractmethod + def analyze_nodes(self, nodes): + """ + Process nodes from get_data and return list of down nodes + :param nodes: dict of metrics of nodes { 'metric1': nodes, + 'metric2': nodes} + :return: a list of down nodes + """ + pass + + @abc.abstractmethod + def process_failed(self, nodes=[], wait=0): + """ + Double check the failed nodes again to make sure that nodes are down. + return a list of down nodes to be passed to the evacuation tool to + process failed hosts. + :param nodes: a list contains pre-checked nodes to re-check them again + :param wait: a configurable a mount of time to wait before doing this + check to give a chance for the host to recover if there was a minor + issue. + :return: a list of nodes to be evacuated, the list will be passed + directly to the evacuation tool to process them + """ + pass + + @abc.abstractmethod + def is_alive(self): + """ + Plugin should provide a way to make sure that the monitoring system is + a live or not. It's optional not all drivers need to implement it. + :return: True or False + """ + raise NotImplementedError() + + def get_info(self): + """ + Get Driver information .. + :return: dict of name, version, author, ... + """ diff --git a/osha/monitors/common/manager.py b/osha/monitors/common/manager.py new file mode 100644 index 0000000..ebc9d6b --- /dev/null +++ b/osha/monitors/common/manager.py @@ -0,0 +1,66 @@ +# (c) Copyright 2014,2015 Hewlett-Packard Development Company, L.P. +# +# 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 +from oslo_log import log +from oslo_utils import importutils + +CONF = cfg.CONF +LOG = log.getLogger(__name__) + + +class MonitorManager(object): + + def __init__(self): + monitor = CONF.get('monitoring') + self.driver = importutils.import_object( + monitor.get('driver'), + monitor.get('username'), + monitor.get('password'), + monitor.get('endpoint'), + **monitor.get('kwargs') + ) + driver_info = self.driver.get_info() + LOG.info('Initializing driver %s with version %s found in %s' % + (driver_info['name'], driver_info['version'], + monitor.get('driver'))) + + def monitor(self): + # Check if the monitoring system is a live + is_alive = self.driver.is_alive() + # if not a live will record that in logs and will try to communicate ! + if not is_alive: + LOG.error('Monitoring system is not a live or may be driver is ' + 'missing implementation for is_alive method') + + # getting data from the monitoring system + # may be in future we add a hock function to external data processors ! + # @todo add external data processors to analyze the monitoring systems + # data to separate monitoring from analysis + data = self.driver.get_data() + + # Asking the driver to analyze the data provided and provide list + # of failed nodes + nodes_down = self.driver.analyze_nodes(nodes=data) + if not nodes_down: + LOG.info('No nodes reported down from %s') + return 0 # for the time being we will exit with no error ! + + LOG.info('Nodes Down are: %s will be double checked again after %s ' + 'seconds' % (str(nodes_down), CONF.wait)) + nodes_to_evacuate = self.driver.process_failed(nodes=nodes_down, + wait=CONF.wait) + return nodes_to_evacuate + + def get_driver_info(self): + return self.driver.get_info() diff --git a/osha/monitors/drivers/__init__.py b/osha/monitors/drivers/__init__.py new file mode 100644 index 0000000..f920945 --- /dev/null +++ b/osha/monitors/drivers/__init__.py @@ -0,0 +1 @@ +__author__ = 'saad' diff --git a/osha/monitors/drivers/osha/__init__.py b/osha/monitors/drivers/osha/__init__.py new file mode 100644 index 0000000..f920945 --- /dev/null +++ b/osha/monitors/drivers/osha/__init__.py @@ -0,0 +1 @@ +__author__ = 'saad' diff --git a/osha/monitors/drivers/osha/driver.py b/osha/monitors/drivers/osha/driver.py new file mode 100644 index 0000000..e23b09d --- /dev/null +++ b/osha/monitors/drivers/osha/driver.py @@ -0,0 +1,131 @@ +# (c) Copyright 2014,2015 Hewlett-Packard Development Company, L.P. +# +# 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 osha.common.osclient import OSClient +from osha.monitors.common.driver import MonitorBaseDriver +from time import sleep +from oslo_config import cfg +from oslo_log import log +from httplib import HTTPConnection, socket +from urlparse import urlparse + +CONF = cfg.CONF +LOG = log.getLogger(__name__) + + +class OshaDriver(MonitorBaseDriver): + + def __init__(self, username, password, endpoint, **kwargs): + super(OshaDriver, self).__init__(username, password, endpoint, kwargs) + client = OSClient( + authurl=endpoint, + username=username, + password=password, + **kwargs + ) + LOG.info("OSClient:: username: %s, password: %s, endpoint: %s, kwargs:" + " %s" % (username, '****', endpoint, kwargs)) + self.client = client + + def get_data(self): + hypervisors = self.client.novahypervisors() + computes = self.client.novacomputes() + agents = self.client.neutronagents() + data = {'hypervisors': hypervisors, + 'computes': computes, + 'agents': agents} + return data + + def process_failed(self, nodes=None, wait=0): + if not wait: + wait = CONF.wait + if not nodes: + return None + sleep(wait) + # @todo do the api call again to get the nodes status again + data = self.get_data() + nodes_down = self.analyze_nodes(nodes=data) + # Thanks Eldar :) for sets + nodes_down_hosts = set([dnode['host'] for dnode in nodes_down]) + return [node for node in nodes if node['host'] in nodes_down_hosts] + + def get_metrics(self): + return ['nova-compute', 'hypervisor', 'neutron-ovs-agent'] + + def analyze_nodes(self, nodes): + # list all down nova compute + nova_down = self.is_nova_service_down(nodes.get('computes')) + # list all down hypervisors + hypervisor_down = self.is_hpyervisor_down(nodes.get('hypervisors')) + # list all down openvswitch agents + agents_down = self.is_neutron_agents_down(nodes.get('agents')) + + nodes_down = [] + for server in hypervisor_down: + ip = server.get('ip') + host = server.get('host') + if host in nova_down and host in agents_down: + node = {'ip': ip, 'host': host} + nodes_down.append(node) + + return nodes_down + + def is_alive(self): + url = urlparse(self.endpoint) + try: + connection = HTTPConnection(host=url.netloc) + connection.request('HEAD', url=url.path) + response = connection.getresponse() + except socket.error: + return False + try: + if getattr(response, 'status') == 200: + return True + except AttributeError: + pass + return False + + def get_info(self): + return { + 'name': 'Osha Native Driver', + 'version': 1.0, + 'author': 'Hewlett-Packard Development Company, L.P' + } + + + def is_hpyervisor_down(self, hypervisors): + down_hosts = [] + for hypervisor in hypervisors: + if hypervisor.get('state') == 'down': + host = {} + host['host'] = hypervisor.get('service').get('host') + host['ip'] = hypervisor.get('host_ip') + down_hosts.append(host) + + return down_hosts + + def is_nova_service_down(self, computes): + down_hosts = [] + for node in computes: + if node.get('state') == 'down' and node.get('status') == 'enabled': + down_hosts.append(node.get('host')) + return down_hosts + + def is_neutron_agents_down(self, agents): + down_hosts = [] + for agent in agents: + if agent.get('admin_state_up') and not agent.get('alive'): + down_hosts.append(agent.get('host')) + + return down_hosts + diff --git a/osha.py b/osha/oshad.py similarity index 95% rename from osha.py rename to osha/oshad.py index c1510ae..6a4b4cb 100644 --- a/osha.py +++ b/osha/oshad.py @@ -1,8 +1,8 @@ #__author__ = 'saad' -from daemon import Daemon import sys import logging as log import time +from osha.common.daemon import Daemon log.basicConfig(filename='osha.log') @@ -35,3 +35,7 @@ if __name__ == '__main__': else: print "Usage %s start|stop|restart" % sys.argv[0] sys.exit(0) + + + + diff --git a/servers.yml b/osha/servers.yml similarity index 100% rename from servers.yml rename to osha/servers.yml diff --git a/yaml_parser.py b/osha/yaml_parser.py similarity index 100% rename from yaml_parser.py rename to osha/yaml_parser.py diff --git a/requirements.txt b/requirements.txt index 99185de..1eeb01a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,8 +2,7 @@ pbr>=0.6,!=0.7,<1.0 python-keystoneclient>=1.2.0,<1.4.0 python-neutronclient>=2.4.0,<2.5.0 python-novaclient>=2.22.0,<2.24.0 -python-openstackclient>=1.0.3,<1.1.0 PyYAML>=3.1.0 oslo.config>=1.9.3,<1.10.0 # Apache-2.0 oslo.i18n>=1.5.0,<1.6.0 # Apache-2.0 -oslo.log>=1.0.0,<1.1.0 # Apache-2.0 \ No newline at end of file +oslo.utils>=1.4.0,!=1.4.1,<1.5.0 diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..1ba9d41 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,37 @@ +[metadata] +name = Osha +version = 0.1 +summary = OpenStack Compute High Availability +description-file = + README.rst +author = OpenStack +author-email = openstack-dev@lists.openstack.org +home-page = http://www.openstack.org/ +classifier = + Environment :: OpenStack + Intended Audience :: Information Technology + Intended Audience :: System Administrators + License :: OSI Approved :: Apache Software License + Operating System :: POSIX :: Linux + Programming Language :: Python + Programming Language :: Python :: 2 + Programming Language :: Python :: 2.7 + +[files] +packages = + osha + +[global] +setup-hooks = + pbr.hooks.setup_hook + + +[pbr] +warnerrors = True + + +[entry_points] +oslo.config.opts = + osha = osha.common.config:list_opts +console_scripts = + osha = osha.main:main \ No newline at end of file diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..056c16c --- /dev/null +++ b/setup.py @@ -0,0 +1,29 @@ +# Copyright (c) 2013 Hewlett-Packard Development Company, L.P. +# +# 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. + +# THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT +import setuptools + +# In python < 2.7.4, a lazy loading of package `pbr` will break +# setuptools if some other modules registered functions in `atexit`. +# solution from: http://bugs.python.org/issue15881#msg170215 +try: + import multiprocessing # noqa +except ImportError: + pass + +setuptools.setup( + setup_requires=['pbr'], + pbr=True)