-
Dear Administrators,
- A compute node failed and freezer-dr DID NOT successfully evacuate.
- Please, find the following details about the host:
- Host: {{ host }}
-
- Tenants:
- {% for tenant in tenants %}
- {{ tenant.get('id') }}
- {% endfor %}
-
-
-
- Instances:
-
-
- |
- Instance Name
- |
-
- IP
- |
-
- {% for instance in instances %}
-
- | {{ instance.get('name') }} |
-
- {% for key, value in instance.get('addresses').iteritems() %}
- {{ key }} :
- {{ value[0].get('addr', 'No IP') }}
- {% endfor %}
- |
-
- {% endfor %}
-
-
-
- Host INFO:
-
- {% for key, value in hypervisor.iteritems() %}
-
- | {{ key }} |
- {{ value }} |
-
- {% endfor %}
-
-
-
- TimeStamp: {{ evacuation_time }}
-
-
-
- Thanks for using Freezer-DR !
-
-
-
-
-
diff --git a/etc/templates/original.jinja b/etc/templates/original.jinja
deleted file mode 100644
index dd6e7e5..0000000
--- a/etc/templates/original.jinja
+++ /dev/null
@@ -1,46 +0,0 @@
-
-
-
-
-
Dear {{ name }},
- One of our compute nodes failed due to some technical problem. Your
- instances (listed below) running in tenant {{ tenant }} is being evacuated to another compute host.
- Instances:
-
-
- |
- Instance Name
- |
-
- IP
- |
-
- {% for instance in instances%}
-
- | {{ instance.get('name') }} |
-
- {% for key, value in instance.get('addresses').iteritems() %}
- {{ key }} :
- {{ value[0].get('addr', 'No IP') }}
- {% endfor %}
- |
-
- {% endfor %}
-
- TimeStamp: {{ evacuation_time }}
-
-
-
- Thanks for using Freezer-DR !
-
-
-
-
-
diff --git a/etc/templates/success.jinja b/etc/templates/success.jinja
deleted file mode 100644
index 248bb21..0000000
--- a/etc/templates/success.jinja
+++ /dev/null
@@ -1,69 +0,0 @@
-
-
-
-
-
Dear Administrators,
- A compute node failed and freezer-dr successfully evacuated these instances running
- on this compute node to another node. Please, find the following details
- about the evacuated host:
- Host: {{ host }}
-
- Tenants:
- {% for tenant in tenants %}
- {{ tenant.get('id') }}
- {% endfor %}
-
-
-
- Instances:
-
-
- |
- Instance Name
- |
-
- IP
- |
-
- {% for instance in instances %}
-
- | {{ instance.get('name') }} |
-
- {% for key, value in instance.get('addresses').iteritems() %}
- {{ key }} :
- {{ value[0].get('addr', 'No IP') }}
- {% endfor %}
- |
-
- {% endfor %}
-
-
-
- Host INFO:
-
- {% for key, value in hypervisor.iteritems() %}
-
- | {{ key }} |
- {{ value }} |
-
- {% endfor %}
-
-
-
- TimeStamp: {{ evacuation_time }}
-
-
-
- Thanks for using Freezer-DR !
-
-
-
-
-
diff --git a/etc/templates/user_error.jinja b/etc/templates/user_error.jinja
deleted file mode 100644
index 79ca132..0000000
--- a/etc/templates/user_error.jinja
+++ /dev/null
@@ -1,46 +0,0 @@
-
-
-
-
-
Dear {{ name }},
- One of our compute nodes failed due to some technical problem. Your
- instances (listed below) are running in tenant {{ tenant }} Failed to Evacuate
- Instances:
-
-
- |
- Instance Name
- |
-
- IP
- |
-
- {% for instance in instances%}
-
- | {{ instance.get('name') }} |
-
- {% for key, value in instance.get('addresses').iteritems() %}
- {{ key }} :
- {{ value[0].get('addr', 'No IP') }}
- {% endfor %}
- |
-
- {% endfor %}
-
- TimeStamp: {{ evacuation_time }}
-
-
-
- Thanks for using freezer-dr !
-
-
-
-
-
\ No newline at end of file
diff --git a/etc/templates/user_success.jinja b/etc/templates/user_success.jinja
deleted file mode 100644
index 426525d..0000000
--- a/etc/templates/user_success.jinja
+++ /dev/null
@@ -1,46 +0,0 @@
-
-
-
-
-
Dear {{ name }},
- One of our compute nodes failed due to some technical problem. Your
- instances (listed below) running in tenant {{ tenant }} Evacuated to another compute host
- Instances:
-
-
- |
- Instance Name
- |
-
- IP
- |
-
- {% for instance in instances%}
-
- | {{ instance.get('name') }} |
-
- {% for key, value in instance.get('addresses').iteritems() %}
- {{ key }} :
- {{ value[0].get('addr', 'No IP') }}
- {% endfor %}
- |
-
- {% endfor %}
-
- TimeStamp: {{ evacuation_time }}
-
-
-
- Thanks for using Freezer-DR !
-
-
-
-
-
\ No newline at end of file
diff --git a/freezer_dr/__init__.py b/freezer_dr/__init__.py
deleted file mode 100644
index 8ea7402..0000000
--- a/freezer_dr/__init__.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# (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.
-
-# Freezer-DR Versions
-
-import pbr.version
-
-
-__version__ = pbr.version.VersionInfo('freezer-dr').version_string()
diff --git a/freezer_dr/common/__init__.py b/freezer_dr/common/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/freezer_dr/common/config.py b/freezer_dr/common/config.py
deleted file mode 100644
index 383dd56..0000000
--- a/freezer_dr/common/config.py
+++ /dev/null
@@ -1,347 +0,0 @@
-"""Manage all configuration the OpenStack way."""
-
-# (c) Copyright 2016 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 sys
-
-from oslo_config import cfg
-from oslo_log import log
-
-from freezer_dr import __version__ as FREEZER_DR_VERSION
-from freezer_dr.common.utils import env
-
-
-CONF = cfg.CONF
-
-
-_MONITORS = [
- cfg.StrOpt('driver',
- default='freezer_dr.monitors.drivers.default.driver.'
- 'StandardDriver',
- help='Driver used to get a status updates of compute nodes'),
- cfg.StrOpt('backend_name',
- help='configuration section name. This should contain your '
- 'monitoring specific configuration options.')
-]
-
-_COMMON = [
- cfg.IntOpt('wait',
- default=30,
- help='Time to wait between different operations')
-]
-
-_FENCER = [
- cfg.StrOpt('credentials-file',
- help='YAML File contains the required credentials for compute '
- 'nodes'),
- cfg.IntOpt('retries',
- default=1,
- help='Number of retries to fence the each compute node. Must be'
- ' at least 1 to try first the soft shutdown'),
- cfg.IntOpt('hold-period',
- default=10,
- help='Time in seconds to wait between retries. Should be '
- 'reasonable amount of time as different servers take '
- 'different times to shut off'),
- cfg.StrOpt('driver',
- default='freezer_dr.fencers.drivers.ipmi.driver.IpmiDriver',
- help='Choose the best fencer driver i.e.(ipmi, libvirt, ..'),
- cfg.DictOpt('options',
- default={},
- help='List of kwargs to customize the fencer operation. You '
- 'fencer driver should support these options. Options '
- 'should be in key:value format')
-]
-
-_KEYSTONE_AUTH_TOKEN = [
- cfg.StrOpt('auth_uri',
- help='OpenStack auth URI i.e. http://controller:5000',
- dest='auth_uri'),
- cfg.StrOpt('auth_url',
- help='OpenStack auth URL i.e. http://controller:35357/v3',
- dest='auth_url'),
- cfg.StrOpt('auth_plugin',
- help='OpenStack auth plugin i.e. ( password, token, ...) '
- 'password is the only available plugin for the time '
- 'being',
- dest='auth_plugin'),
- cfg.StrOpt('username',
- help='OpenStack username',
- dest='username'),
- cfg.StrOpt('password',
- help='OpenStack Password',
- dest='password'),
- cfg.StrOpt('project_name',
- help='OpenStack Project Name.',
- dest='project_name'),
- cfg.StrOpt('domain_name',
- help='OpenStack domain Name.',
- dest='domain_name'),
- cfg.StrOpt('project_domain_id',
- help='OpenStack Project Domain id, default is Default',
- dest='project_domain_id'),
- cfg.StrOpt('user_domain_id',
- help='OpenStack user Domain id, default is Default',
- dest='user_domain_id'),
- cfg.StrOpt('project_domain_name',
- help='OpenStack Project Domain name, default is Default',
- dest='project_domain_name'),
- cfg.StrOpt('user_domain_name',
- help='OpenStack user Domain name, default is Default',
- dest='user_domain_name'),
- cfg.DictOpt('kwargs',
- help='OpenStack Authentication arguments you can pass it here'
- ' as Key:Value, Key1:Value1, ... ',
- dest='kwargs',
- default={})
-]
-
-_EVACUATION = [
- cfg.StrOpt('driver',
- default='freezer_dr.evacuators.drivers.default.standard.'
- 'StandardEvacuator',
- help='Time in seconds to wait between retries to disable '
- 'compute node or put it in maintenance mode. Default '
- '10 seconds',
- dest='driver'),
- cfg.IntOpt('wait',
- default=10,
- help='Time in seconds to wait between retries to disable '
- 'compute node or put it in maintenance mode. Default '
- '10 seconds',
- dest='wait'),
- cfg.IntOpt('retries',
- default=1,
- help='Number of retries to put node in maintenance mode before'
- ' reporting failure to evacuate the node',
- dest='retries'),
- cfg.BoolOpt('shared-storage',
- default=False,
- help='Set this option to True in case your compute nodes are '
- 'running on a shared storage or False if not',
- dest='shared_storage'),
- cfg.DictOpt('options',
- default={},
- help='Dict contains kwargs to be passed to the evacuator '
- 'driver. In case you have additional args needs to be '
- 'passed to your evacuator please, list them as '
- 'key0:value0, key1:value1, ...',
- dest='options')
-]
-
-_NOTIFIERS = [
- cfg.StrOpt('driver',
- default='freezer_dr.notifiers.drivers.default.default_email.'
- 'StandardEmail',
- dest='driver',
- help='Notification driver to load it to notify users '
- 'if something went wrong. There are two supported drivers'
- ': freezer_dr.notifiers.drivers.default.default_email.'
- 'StandardEmail and freezer_dr.notifiers.drivers.default.'
- 'slack.slack.SlackNotifier'),
- cfg.StrOpt('endpoint',
- default='localhost',
- dest='endpoint',
- help='Endpoint URL for the notification system. If you the '
- 'driver you are using doesnot require any URL just comment'
- ' it or use none'),
- cfg.StrOpt('username',
- default=None,
- dest='username',
- help='Username to authenticate against the notification system.'
- ' If the driver you are using doesnot require any '
- 'authentications comment or use None'),
- cfg.StrOpt('password',
- default=None,
- dest='password',
- help='Password to authenticate against the notification system. '
- 'If the driver you are using doesnot require any '
- 'authentications comment or use None'),
- cfg.StrOpt('templates-dir',
- dest='templates-dir',
- default='/etc/freezer/templates',
- help='Path to Jinja2 templates directory that contains '
- 'message templates'),
- cfg.DictOpt('options',
- default={},
- dest='options',
- help='Key:Value Kwargs to pass it to the notification driver, '
- 'if you want to pass any special arguments for your '
- 'driver. If you want to use the SlackNotifier driver, '
- 'set as: options = slack_timeout:512,'
- 'slack_ca_certs:/ca.crt,slack_insecured:True'),
- cfg.ListOpt('notify-list',
- default=[],
- dest='notify-list',
- help='List of emails to sent them notification if something '
- 'went wrong and Freezer DR wasnot able to send an email '
- 'to the tenant admin'),
- cfg.StrOpt('notify-from',
- dest='notify-from',
- help='The sender address, it can be email address if we used '
- 'default email driver, or phone number if we use sms '
- 'gateway for example.')
-]
-
-
-def build_os_options():
- """Build oslo options related to OpenStack environment."""
- 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-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():
- """Register configuration."""
- 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')
-
- fencers_grp = cfg.OptGroup('fencer',
- title='fencer Options',
- help='fencer Driver/plugin to be used to '
- 'fence compute nodes')
- CONF.register_group(fencers_grp)
- CONF.register_opts(_FENCER, group='fencer')
-
- # Evacuation Section :)
- evacuators_grp = cfg.OptGroup('evacuation',
- title='Evacuation Options',
- help='Evacuation Driver/plugin opts to be '
- 'used to Evacuate compute nodes')
- CONF.register_group(evacuators_grp)
- CONF.register_opts(_EVACUATION, group='evacuation')
-
- # Notification Section :)
- notifiers_grp = cfg.OptGroup('notifiers',
- title='Notification Options',
- help='Notification Driver/plugin opts to be '
- 'used to Notify admins/users if failure'
- ' happens')
- CONF.register_group(notifiers_grp)
- CONF.register_opts(_NOTIFIERS, group='notifiers')
-
- # Keystone Auth
- keystone_grp = cfg.OptGroup('keystone_authtoken',
- title='Keystone Auth Options',
- help='OpenStack Credentials to call the nova '
- 'APIs to evacuate ')
- CONF.register_group(keystone_grp)
- CONF.register_opts(_KEYSTONE_AUTH_TOKEN, group='keystone_authtoken')
-
- default_conf = cfg.find_config_files('freezer', 'freezer-dr', '.conf')
- log.register_options(CONF)
-
- CONF(args=sys.argv[1:],
- project='freezer',
- default_config_files=default_conf,
- version=FREEZER_DR_VERSION)
-
-
-def setup_logging():
- """Set some oslo log defaults."""
- _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', 'freezer-dr=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, 'freezer-dr', version=FREEZER_DR_VERSION)
-
-
-def list_opts():
- _OPTS = {
- None: _COMMON,
- 'monitoring': _MONITORS,
- 'keystone_authtoken': _KEYSTONE_AUTH_TOKEN,
- 'fencer': _FENCER,
- 'evacuation': _EVACUATION,
- 'notifiers': _NOTIFIERS
- }
-
- return _OPTS.items()
diff --git a/freezer_dr/common/daemon.py b/freezer_dr/common/daemon.py
deleted file mode 100644
index d0bcf13..0000000
--- a/freezer_dr/common/daemon.py
+++ /dev/null
@@ -1,182 +0,0 @@
-#!/usr/bin/env python
-"""Generic deamon."""
-# (c) Copyright 2016 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 atexit
-import logging as log
-import os
-import sys
-import time
-
-from signal import SIGTERM
-
-
-class Daemon(object):
-
- """A generic daemon class.
-
- Usage: subclass the Daemon class and override the run() method
- """
-
- def __init__(self, pidfile, stdin='/dev/null', stdout='/dev/null',
- stderr='/dev/null'):
- """Instantiantion."""
- self.stdin = stdin
- self.stdout = stdout
- self.stderr = stderr
- self.pidfile = pidfile
-
- def daemonize(self):
- """Do the UNIX double-fork magic.
-
- See Stevens' "Advanced Programming in the UNIX Environment" for
- details (ISBN 0201563177)
- http://www.erlenstar.demon.co.uk/unix/faq_2.html#SEC16
- """
- try:
- pid = os.fork()
- if pid > 0:
- # exit first parent
- sys.exit(0)
- except OSError as e:
- sys.stderr.write("fork #1 failed: %d (%s)\n" %
- (e.errno, e.strerror))
- log.error(e)
- sys.exit(1)
-
- # decouple from parent environment
- os.chdir("/")
- os.setsid()
- os.umask(0)
-
- # do second fork
- try:
- pid = os.fork()
- if pid > 0:
- # exit from second parent
- sys.exit(0)
- except OSError as e:
- sys.stderr.write("fork #2 failed: %d (%s)\n"
- % (e.errno, e.strerror))
- log.error(e)
- sys.exit(1)
-
- # redirect standard file descriptors
- sys.stdout.flush()
- sys.stderr.flush()
- si = open(self.stdin, 'r')
- so = open(self.stdout, 'a+')
- se = open(self.stderr, 'a+', 0)
- os.dup2(si.fileno(), sys.stdin.fileno())
- os.dup2(so.fileno(), sys.stdout.fileno())
- os.dup2(se.fileno(), sys.stderr.fileno())
-
- # write pidfile
- atexit.register(self.delpid)
-
- pid = str(os.getpid())
- f = open(self.pidfile, 'w+')
- f.write("%s\n" % pid)
- f.close()
-
- def delpid(self):
- """Delete PID file."""
- os.remove(self.pidfile)
-
- def start(self):
- """Start the daemon."""
- log.error("Test")
- # Check for a pidfile to see if the daemon already runs
- try:
- pf = open(self.pidfile, 'r')
- pid = int(pf.read().strip())
- pf.close()
- except IOError:
- pid = None
-
- if pid:
- message = "pidfile %s already exist. Daemon" \
- " already running?\n"
- sys.stderr.write(message % self.pidfile)
- sys.exit(1)
-
- # Start the daemon
- self.daemonize()
- self.run()
-
- # @todo needs some enhancement like check /proc/%pid/status if it's
- # really running or not ! may be it's killed by external process
- # the PID won't be updated !
- def status(self):
- """Check daemon status."""
- try:
- pf = open(self.pidfile, 'r')
- pid = int(pf.read().strip())
- pf.close()
- except IOError:
- pid = None
-
- if pid:
- message = "pidfile %s already exist. Daemon already " \
- "running. PID: %d \n"
- sys.stdout.write(message % (self.pidfile, pid))
- sys.exit(0)
- else:
- message = "Service not running!\n"
- sys.stdout.write(message)
- sys.exit(0)
-
- def stop(self):
- """Stop the daemon."""
- # Get the pid from the pidfile
- try:
- pf = open(self.pidfile, 'r')
- pid = int(pf.read().strip())
- pf.close()
- except IOError:
- pid = None
-
- if not pid:
- message = "pidfile %s does not exist." \
- " Daemon not running?\n"
- sys.stderr.write(message % self.pidfile)
- return # not an error in a restart
-
- # Try killing the daemon process
- try:
- while 1:
- os.kill(pid, SIGTERM)
- time.sleep(0.1)
- except OSError as err:
- err = str(err)
- if err.find("No such process") > 0:
- if os.path.exists(self.pidfile):
- os.remove(self.pidfile)
- else:
- print(str(err))
- sys.exit(1)
-
- def restart(self):
- """Restart the daemon."""
- self.stop()
- self.start()
-
- def run(self):
- """You should override this method when you subclass Daemon.
-
- It will be called after the process has been
- daemonized by start() or restart().
- """
diff --git a/freezer_dr/common/osclient.py b/freezer_dr/common/osclient.py
deleted file mode 100644
index 38efe22..0000000
--- a/freezer_dr/common/osclient.py
+++ /dev/null
@@ -1,311 +0,0 @@
-"""OpenStack client class."""
-# (c) Copyright 2016 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 import session
-
-from keystoneclient.auth.identity import v3
-
-from keystoneclient import client as keystoneclient
-
-from neutronclient.v2_0 import client as neutronclient
-
-from novaclient import client as novaclient
-
-from oslo_log import log
-
-
-LOG = log.getLogger(__name__)
-
-
-class OSClient:
- """Provide OpenStack credentials to initalize the connection."""
-
- def __init__(self, authurl, authmethod='password', ** kwargs):
- """Initialize the all class vars.
-
- :param authmethod: string authmethod should be password or token but
- currently we support only password !
- :param kwargs: username, user_id, project_name, project_id,
- default_domain_id,
- """
- self.authmethod = authmethod
- self.authurl = authurl
- self.auth_session = None
- self.endpoint_type = 'internalURL'
- self.interface = 'internal'
- self.verify = True
- self.insecure = kwargs.pop('insecure', False)
- if self.insecure:
- self.verify = not bool(self.insecure)
-
- if authmethod == 'password':
- if 'endpoint_type' in kwargs:
- self.endpoint_type = kwargs.pop('endpoint_type', 'internalURL')
- if 'interface' in kwargs:
- self.interface = kwargs.pop('interface', 'internal')
- self.kwargs = kwargs
- # self.username = kwargs.get('username', None)
- # self.password = kwargs.get('password')
- # self.project_name = kwargs.get('project_name', None)
- # self.project_id = kwargs.get('project_id', None)
- # self.user_id = kwargs.get('user_id', None)
- # self.user_domain_id = kwargs.get('user_domain_id', None)
- # self.user_domain_name = kwargs.get('user_domain_name', None)
- # self.project_domain_name =
- # kwargs.get('project_domain_name', None)
- # self.endpoint_type = kwargs.get('endpoint_type', 'internalURL')
- else:
- print("The available authmethod is password for the time being")
- print("Please, provide a password credential.")
-
- self.auth()
-
- def auth(self):
- """Create a session."""
- auth = v3.Password(auth_url=self.authurl, reauthenticate=True,
- **self.kwargs)
- self.auth_session = session.Session(auth=auth, verify=self.verify)
-
- def get_novaclient(self):
- if not hasattr(self, 'nova'):
- self.auth()
- self.nova = novaclient.Client('2', session=self.auth_session,
- endpoint_type=self.endpoint_type,
- insecure=self.insecure)
- return self.nova
-
- def get_neutronclient(self):
- if not hasattr(self, 'neutron'):
- self.auth()
- self.neutron = neutronclient.Client(
- session=self.auth_session,
- endpoint_type=self.endpoint_type,
- insecure=self.insecure
- )
- return self.neutron
-
- def novacomputes(self):
- nova = self.get_novaclient()
- services = nova.services.list()
- compute_nodes = []
- compute_hosts = []
- for service in services:
- service = service.to_dict()
- if service.get('binary') == 'nova-compute':
- compute_nodes.append(service)
- compute_hosts.append(service.get('host'))
- self.compute_hosts = compute_hosts
- return compute_nodes
-
- def novahypervisors(self):
- nova = self.get_novaclient()
- hypervisors = nova.hypervisors.list()
- nova_hypervisors = []
-
- for hypervisor in hypervisors:
- nova_hypervisors.append(hypervisor.to_dict())
- return nova_hypervisors
-
- def neutronagents(self, hosts=[]):
- if not hosts:
- hosts = self.compute_hosts
- neutron = self.get_neutronclient()
- agents = neutron.list_agents()
- neutron_agents = []
- for agent in agents.get('agents'):
- if agent.get('host') in hosts and agent.get('binary') == \
- 'neutron-openvswitch-agent':
- neutron_agents.append(agent)
-
- return neutron_agents
-
- def evacuate(self, nodes, shared_storage=False):
- """
- Will get the hypervisors and list all running VMs on it and then start
- Evacuating one by one ...
- :param nodes: List of nodes to be evacuated !
- :param shared_storage: Boolean, True if your compute nodes are running
- under shared storage and False otherwise
- :return: List of nodes with VMs that were running on that node
- """
- nova = self.get_novaclient()
- evacuated_nodes = []
- for node in nodes:
- hypervisors = nova.hypervisors.search(node.get('host'), True)
- for hypervisor in hypervisors:
- if not hasattr(hypervisor, 'servers'):
- break
- for server in hypervisor.servers:
- try:
- nova.servers.evacuate(server.get('uuid'),
- on_shared_storage=shared_storage)
- except Exception as e:
- LOG.error(e)
- host = {'host': node.get(
- 'host'), 'servers': hypervisor.servers}
- evacuated_nodes.append(host)
- return evacuated_nodes
-
- def set_in_maintenance(self, nodes):
- """Set compute nodes in maintenance mode."""
- nova = self.get_novaclient()
- for node in nodes:
- output = []
- host = nova.hosts.get(node)[0]
- values = {"maintenance_mode": "enable"}
- try:
- output.append(host.update(values))
- except Exception as e:
- LOG.error(e)
- return output
-
- def get_session(self):
- """Get the authentication section."""
- auth_session = session.Session(auth=self.auth_session.auth,
- verify=self.verify)
- return auth_session
-
- def get_node_status(self, node):
- """
- Check the node nova-service status and if it's disabled or not.
- :param node: dict contains node info
- :return: True or False. True => node disabled, False => node is enabled
- or unknow status !
- """
- nova = self.get_novaclient()
- try:
- node_service = nova.services.find(host=node.get('host'))
- del nova
- except Exception as e:
- LOG.error(e)
- return False
-
- if not node_service:
- return False
- node = node_service.to_dict()
- if node.get('status') == 'disabled':
- return True
- return False
-
- def disable_node(self, node):
- """Disable nova on the failing node."""
- nova = self.get_novaclient()
- try:
- node_service = nova.services.find(host=node.get('host'))
- except Exception as e:
- LOG.error(e)
- return False
-
- if not node_service:
- return False
- node = node_service.to_dict()
- del node_service
- try:
- nova.services.disable_log_reason(
- host=node.get('host'),
- binary=node.get('binary'),
- reason='Host failed and needs to be evacuated.'
- )
- del nova
- LOG.info('Compute host: %s has been disabled to be evacuated. '
- 'Host details: %s' % (node.get('host'), str(node)))
- except Exception as e:
- LOG.error(e)
- return False
- return True
-
- def get_hypervisor_instances(self, node):
- """Get instances from an hypervisor."""
- nova = self.get_novaclient()
- hypervisors = nova.hypervisors.search(node.get('host'), True)
- if not hypervisors:
- return []
- return hypervisors[0].servers
-
- def get_hypervisor(self, node):
- """Get an instance of the hypervisor.
-
- :param node: dict contains host index
- :return: Hypervisor
- """
- nova = self.get_novaclient()
- hypervisors = nova.hypervisors.search(node.get('host'), True)
- if not hypervisors:
- return None
- return hypervisors[0]
-
- def get_instances_list(self, node):
- """Get instances running on a node for all tenants."""
- nova = self.get_novaclient()
- servers = nova.servers.list(detailed=True,
- search_opts={'host': node.get('host'),
- 'all_tenants': True})
- servers_data = []
- for server in servers:
- servers_data.append(server.to_dict())
-
- return servers_data
-
- def get_affected_tenants(self, node):
- return self.get_instances_list(node)
-
- def list_tenants(self):
- """List tenants."""
- auth_session = session.Session(auth=self.auth_session.auth)
- keystone = keystoneclient.Client(session=auth_session,
- endpoint_type=self.endpoint_type)
- projects = keystone.projects.list()
-
- projects_data = []
- for project in projects:
- projects_data.append(project.to_dict())
-
- return projects_data
-
- def users_on_tenant(self, tenant):
- """List user per project."""
- auth_session = session.Session(auth=self.auth_session.auth,
- verify=self.verify)
- keystone = keystoneclient.Client(session=auth_session,
- endpoint_type=self.endpoint_type,
- interface='internal',
- insecure=self.insecure)
- users = []
- try:
- users = keystone.users.list(default_project=tenant)
- except Exception as e:
- print(e)
- users_list = []
- for user in users:
- users_list.append(user.to_dict())
-
- return users_list
-
- def get_hypervisors_stats(self):
- """Get stats for all hypervisors."""
- nova = self.get_novaclient()
- stats = nova.hypervisor_stats.statistics()
- return stats.to_dict()
-
- def get_hypervisor_details(self, node):
- """Get details about hypervisor running on the provided node."""
- nova = self.get_novaclient()
- hypervisors = nova.hypervisors.list(detailed=True)
- for hypervisor in hypervisors:
- hypervisor = hypervisor.to_dict()
- if hypervisor.get('hypervisor_hostname') == node.get('host'):
- return hypervisor
-
- return None
diff --git a/freezer_dr/common/utils.py b/freezer_dr/common/utils.py
deleted file mode 100644
index abe69b6..0000000
--- a/freezer_dr/common/utils.py
+++ /dev/null
@@ -1,97 +0,0 @@
-"""Utility functions shared from all modules into the project."""
-# (c) Copyright 2016 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
-
-import jinja2
-
-from oslo_config import cfg
-from oslo_log import log
-
-from freezer_dr.common.osclient import OSClient
-
-
-CONF = cfg.CONF
-LOG = log.getLogger(__name__)
-
-
-def env(*env_vars, **kwargs):
- """Get all environment variables."""
- for variable in env_vars:
- value = os.environ.get(variable, None)
- if value:
- return value
- return kwargs.get('default', '')
-
-
-def get_os_client():
- """Return the OpenStack client.
-
- Loads credentials from [keystone_authtoken] section in the configuration
- file and initialize the client and return an instance of the client
- :return: Initialized instance of OS Client
- """
- credentials = CONF.get('keystone_authtoken')
- client = OSClient(
- authurl=credentials.get('auth_url'),
- username=credentials.get('username'),
- password=credentials.get('password'),
- project_name=credentials.get('project_name'),
- user_domain_id=credentials.get('user_domain_id'),
- project_domain_id=credentials.get('project_domain_id'),
- project_domain_name=credentials.get('project_domain_name'),
- user_domain_name=credentials.get('user_domain_name'),
- **credentials.get('kwargs')
- )
-
- return client
-
-
-def load_jinja_templates(template_dir, template_name, template_vars):
- """Load and render existing Jinja2 templates.
-
- The main purpose of the function is to prepare the message to be sent and
- render it for the driver to send it directly.
-
- :param template_dir: Location where jinja2 templates are stored
- :param template_name: name of the template to load it
- :param template_vars: Dict to replace existing vars in the template with
- values.
- :return: String message
- """
- template_loader = jinja2.FileSystemLoader(searchpath=template_dir)
- template_env = jinja2.Environment(loader=template_loader)
- template = template_env.get_template(template_name)
- return template.render(template_vars)
-
-
-def get_admin_os_client():
- """Return admin client data.
-
- Loads credentials from [keystone_authtoken] section in the configuration
- file and initialize the client with admin privileges and return
- an instance of the client
- :return: Initialized instance of OS Client
- """
- credentials = CONF.get('keystone_authtoken')
- client = OSClient(
- authurl=credentials.get('auth_url'),
- username=credentials.get('username'),
- password=credentials.get('password'),
- domain_name=credentials.get('domain_name'),
- user_domain_id=credentials.get('user_domain_id'),
- user_domain_name=credentials.get('user_domain_name'),
- **credentials.get('kwargs')
- )
- return client
diff --git a/freezer_dr/common/yaml_parser.py b/freezer_dr/common/yaml_parser.py
deleted file mode 100644
index 9a42b98..0000000
--- a/freezer_dr/common/yaml_parser.py
+++ /dev/null
@@ -1,72 +0,0 @@
-# (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
-import yaml
-
-
-class YamlParser(object):
-
- _INDEX = 'servers'
-
- def __init__(self, yml_file, index='servers'):
- """
- Provide Yaml file to parse it and process data
- :param yml_file: path to yaml file
- :param index: the key in the .yml file to get all servers listed under
- this key. the default 'is servers'
- """
- self.file = yml_file
- self._INDEX = index
- self.data = self.parse()
-
- def parse(self):
- if not self.file:
- raise Exception('No file specified !')
- if not os.path.exists(self.file) or not os.path.isfile(self.file):
- raise Exception('File desnot exists')
-
- stream = open(self.file, 'r')
- data = yaml.load(stream)
- return data
-
- def find_server_by_ip(self, ip):
- """
- get server information ilo username, password and ip
- :param ip: mgmt ip address of the server, this should be the same like
- the ip in the .yml file
- :return: dict contains server information
- """
- return self.find_server('ip-addr', ip)
-
- def find_server_by_hostname(self, hostname):
- """
- get server information ilo username, password and ip
- :param hostname: hostname matches one of the ones in the .yml file
- :return: dict contains the server information
- """
- return self.find_server(key='hostname', value=hostname)
-
- def find_server(self, key, value):
- """
- Generic function to query the .yml file to get server information by any
- key.
- :param key:
- :param value:
- :return:
- """
- for server in self.data.get(self._INDEX):
- if server.get(key) == value:
- return server
-
- return None
diff --git a/freezer_dr/evacuators/__init__.py b/freezer_dr/evacuators/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/freezer_dr/evacuators/common/__init__.py b/freezer_dr/evacuators/common/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/freezer_dr/evacuators/common/driver.py b/freezer_dr/evacuators/common/driver.py
deleted file mode 100644
index 8fc6d8a..0000000
--- a/freezer_dr/evacuators/common/driver.py
+++ /dev/null
@@ -1,49 +0,0 @@
-# (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
-
-
-class EvacuatorBaseDriver(object, metaclass=abc.ABCMeta):
- """
- Abstract class for all evacuation drivers should implement to have
- a unified interface
- """
-
- def __init__(self, nodes, evacuator_conf, fencer):
- """
- Initialize Evacuation driver with the config args
- :param nodes: A list of nodes to be evacuated!
- :param evacuator_conf: A dict of arguments that got loaded from the
- configuration file!
- :return: None
- """
- self.nodes = nodes
- self.evacuator_conf = evacuator_conf
- self.fencer = fencer
-
- @abc.abstractmethod
- def evacuate(self, enable_fencing=True):
- """Evacuate the infected node.
- :return: Two lists; the first one will be the succeeded nodes and the
- other is the failed nodes
- """
- pass
-
- @abc.abstractmethod
- def get_info(self):
- """
- Get Driver Information
- :return: Dict contains driver information
- """
- pass
diff --git a/freezer_dr/evacuators/common/manager.py b/freezer_dr/evacuators/common/manager.py
deleted file mode 100644
index 880ccda..0000000
--- a/freezer_dr/evacuators/common/manager.py
+++ /dev/null
@@ -1,48 +0,0 @@
-# (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 freezer_dr.evacuators.common.utils import get_nodes_details
-from freezer_dr.fencers.common.manager import FencerManager
-from oslo_config import cfg
-from oslo_log import log
-from oslo_utils import importutils
-from time import sleep
-
-CONF = cfg.CONF
-LOG = log.getLogger(__name__)
-
-
-class EvacuationManager(object):
-
- def __init__(self, enable_fencing=True):
- self.enable_fencing = enable_fencing
-
- def evacuate(self, nodes):
- fencer = FencerManager(nodes)
- evacuation_conf = CONF.get('evacuation')
- driver = importutils.import_object(
- evacuation_conf['driver'],
- nodes,
- evacuation_conf,
- fencer
- )
-
- return driver.evacuate(self.enable_fencing)
-
- def get_nodes_details(self, nodes):
- """
- To be re-structured after fixing the nova bug !
- :param nodes: list of nodes
- :return: list of node with more details
- """
- return get_nodes_details(nodes)
diff --git a/freezer_dr/evacuators/common/utils.py b/freezer_dr/evacuators/common/utils.py
deleted file mode 100644
index a514b7c..0000000
--- a/freezer_dr/evacuators/common/utils.py
+++ /dev/null
@@ -1,62 +0,0 @@
-# (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 freezer_dr.common.utils import get_admin_os_client
-from freezer_dr.common.utils import get_os_client
-
-
-def get_nodes_details(nodes):
- """
- Get the hypervisor details, instances running on it, tenants
- :param nodes: list of hypervisors
- :return: List of hypervisors with detailed information
- """
- nodes_details = []
- client = get_os_client()
- for node in nodes:
- instances = client.get_instances_list(node)
- tenants = set([instance.get('tenant_id') for instance in instances])
- node['instances'] = instances
- node['tenants'] = tenants
- node['details'] = client.get_hypervisor_details(node)
- nodes_details.append(node)
- nodes_details = get_users_on_tenants(nodes_details)
- return nodes_details
-
-
-def get_users_on_tenants(nodes):
- """
- Lists all users that have access to a certain tenant.
- REQUIRE ADMIN PRIVILEGES !
- :param nodes: list of hypervisors
- :return: List of hypervisors with detailed tenant info
- """
- details = []
- client = get_admin_os_client()
- for node in nodes:
- if 'tenants' in node:
- tenants = []
- for tenant in node.get('tenants'):
- users = client.users_on_tenant(tenant)
- tenants.append(
- {'id': tenant,
- 'users': users,
- 'instances': [instance for instance in
- node.get('instances') if
- instance.get('tenant_id') == tenant]})
- node['tenants'] = tenants
- details.append(node)
- return details
-
-
-
diff --git a/freezer_dr/evacuators/drivers/__init__.py b/freezer_dr/evacuators/drivers/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/freezer_dr/evacuators/drivers/default/__init__.py b/freezer_dr/evacuators/drivers/default/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/freezer_dr/evacuators/drivers/default/standard.py b/freezer_dr/evacuators/drivers/default/standard.py
deleted file mode 100644
index 885e20f..0000000
--- a/freezer_dr/evacuators/drivers/default/standard.py
+++ /dev/null
@@ -1,117 +0,0 @@
-# (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 freezer_dr.common.utils import get_os_client
-from freezer_dr.evacuators.common.driver import EvacuatorBaseDriver
-from oslo_config import cfg
-from oslo_log import log
-import time
-
-CONF = cfg.CONF
-LOG = log.getLogger(__name__)
-
-
-class StandardEvacuator(EvacuatorBaseDriver):
-
- def __init__(self, nodes, evacuator_conf, fencer):
- super(StandardEvacuator, self).__init__(nodes, evacuator_conf, fencer)
- # initialize the OS client!
- self.client = get_os_client()
- self.wait = evacuator_conf.get('wait')
- self.retires = evacuator_conf.get('retries', 1)
- if self.retires <= 0:
- self.retires = 1
-
- def _disable_node(self, node):
- if not self.is_node_disabled(node):
- return self.disable_node(node)
- else:
- True
-
- def evacuate(self, enable_fencing=True):
- # try to disable node
- # @todo needs more error handling like if the status didn't update or
- # we are unable to disable the node ???
- failed_nodes = [] # maintain nodes that are going to fail at any state
- succeeded_nodes = []
- for node in self.nodes:
- status = False
- for i in range(0, self.retires):
- status = self._disable_node(node)
- # if True ( node disabled ) break the loop
- if status:
- break
- else:
- status = False
- node['status'] = status
- # make sure the disable request was successful
- if not self.get_node_status(node):
- # if the node failed at any step no reason to move it to
- # the next step
- failed_nodes.append(node)
- self.nodes.remove(node) #
- else:
- succeeded_nodes.append(node)
-
- nodes = succeeded_nodes
- if enable_fencing:
- nodes = self.fencer.fence(nodes=nodes)
- """
- @todo this code needs to be commented for the time being till we fix
- nova bug found in state, which always go up afer enable or disable. We
- will use get_node_details for the time being from the main script to
- get nodes details before evacuating ...
- succeeded_nodes = []
- for node in nodes:
- node['instances'] = self.driver.get_node_instances(node)
- succeeded_nodes.append(node)
-
- nodes = succeeded_nodes
- """
- # Start evacuation calls ...
- evacuated_nodes = []
- for i in range(0, self.retires):
- try:
- time.sleep(self.wait)
- nodes = self.evacuate_nodes(nodes)
- if not nodes:
- break
- evacuated_nodes = nodes
- except Exception as e:
- LOG.error(e)
-
- return evacuated_nodes, failed_nodes
-
- def get_node_instances(self, node):
- return self.client.get_hypervisor_instances(node)
-
- def disable_node(self, node):
- return self.client.disable_node(node)
-
- def get_node_status(self, node):
- return self.client.get_node_status(node)
-
- def is_node_disabled(self, node):
- return self.client.get_node_status(node)
-
- def evacuate_nodes(self, nodes):
- return self.client.evacuate(
- nodes, shared_storage=self.evacuator_conf['shared_storage'])
-
- def get_info(self):
- """
- To be implemented.
- Get Driver Information
- :return: Dict contains driver information
- """
- raise NotImplementedError
diff --git a/freezer_dr/evacuators/drivers/dummy/__init__.py b/freezer_dr/evacuators/drivers/dummy/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/freezer_dr/evacuators/drivers/dummy/dummy.py b/freezer_dr/evacuators/drivers/dummy/dummy.py
deleted file mode 100644
index 615dc65..0000000
--- a/freezer_dr/evacuators/drivers/dummy/dummy.py
+++ /dev/null
@@ -1,41 +0,0 @@
-# (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 freezer_dr.evacuators.common.driver import EvacuatorBaseDriver
-
-
-class DummyEvacuator(EvacuatorBaseDriver):
- """Evacuation driver that does nothing. Useful for testing other parts
- of Freezer-DR.
- """
-
- def __init__(self, nodes, evacuator_conf, fencer):
- super(DummyEvacuator, self).__init__(nodes, evacuator_conf, fencer)
-
- def disable_node(self, node):
- return True
-
- def get_node_status(self, node):
- return False
-
- def is_node_disabled(self, node):
- return True
-
- def evacuate_nodes(self, nodes):
- return nodes
-
- def get_node_instances(self, node):
- raise NotImplementedError
-
- def get_info(self):
- raise NotImplementedError
diff --git a/freezer_dr/fencers/__init__.py b/freezer_dr/fencers/__init__.py
deleted file mode 100644
index f920945..0000000
--- a/freezer_dr/fencers/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-__author__ = 'saad'
diff --git a/freezer_dr/fencers/common/__init__.py b/freezer_dr/fencers/common/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/freezer_dr/fencers/common/driver.py b/freezer_dr/fencers/common/driver.py
deleted file mode 100644
index b1dfb9b..0000000
--- a/freezer_dr/fencers/common/driver.py
+++ /dev/null
@@ -1,53 +0,0 @@
-# (c) Copyright 2016 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.
-
-"""Abstract fencer"""
-
-import abc
-
-
-class FencerBaseDriver(object, metaclass=abc.ABCMeta):
-
- """Abstract class that all fencer plugins.
-
- Should be implemented to have a unified interface and as many plugins as
- needed.
- """
-
- def __init__(self, nodes, fencer_conf):
- """Initialize the driver.
-
- Any fencer driver requires the following parameters to do the api
- calls. All these parameters can be passed from the configuration
- file in /etc/freezer/dr.conf (default).
-
- :param nodes: A list of failed nodes to be fenced!
- :param fencer_conf: dict contains configuration options loaded
- from the config file.
- """
- self.nodes = nodes
- self.fencer_conf = fencer_conf
-
- @abc.abstractmethod
- def fence(self):
- """This function to be implemented by each driver. Each driver will
- implement its own fencing logic and the manager will just load it and
- call the fence function"""
-
- @abc.abstractmethod
- def get_info(self):
- """Get Driver information.
-
- :return: dict of name, version, author, ...
- """
diff --git a/freezer_dr/fencers/common/manager.py b/freezer_dr/fencers/common/manager.py
deleted file mode 100644
index 6a3431f..0000000
--- a/freezer_dr/fencers/common/manager.py
+++ /dev/null
@@ -1,46 +0,0 @@
-# (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 FencerManager(object):
-
- def __init__(self, nodes):
- self.fencer_conf = CONF.get('fencer')
- self.nodes = nodes
-
- def fence(self, nodes=None):
- """
- Try to shutdown nodes and wait for configurable amount of times
- :return: list of nodes and either they are shutdown or failed
- """
- # update the list of nodes if required!
- if nodes:
- self.nodes = nodes
- driver_name = self.fencer_conf['driver']
- driver = importutils.import_object(
- driver_name,
- self.nodes,
- self.fencer_conf
- )
- LOG.debug('Loaded fencing driver {0} with config: '
- '{1}'.format(driver.get_info(), self.fencer_conf))
-
- return driver.fence()
diff --git a/freezer_dr/fencers/drivers/__init__.py b/freezer_dr/fencers/drivers/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/freezer_dr/fencers/drivers/ipmi/__init__.py b/freezer_dr/fencers/drivers/ipmi/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/freezer_dr/fencers/drivers/ipmi/driver.py b/freezer_dr/fencers/drivers/ipmi/driver.py
deleted file mode 100644
index 3229395..0000000
--- a/freezer_dr/fencers/drivers/ipmi/driver.py
+++ /dev/null
@@ -1,115 +0,0 @@
-# (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 freezer_dr.common.yaml_parser import YamlParser
-from freezer_dr.fencers.common.driver import FencerBaseDriver
-from freezer_dr.fencers.drivers.ipmi.ipmitool import IpmiInterface
-from oslo_config import cfg
-from oslo_log import log
-import time
-
-CONF = cfg.CONF
-LOG = log.getLogger(__name__)
-
-
-class IpmiDriver(FencerBaseDriver):
-
- def __init__(self, nodes, fencer_conf):
-
- super(IpmiDriver, self).__init__(nodes, fencer_conf)
- self.parser = YamlParser(self.fencer_conf['credentials_file'])
-
- def prepare_node(self, node):
- """Prepares the subprocess to call ``ipmitool`` with the node details!
- :param node: dict contains node fencing information
- """
- self.interface = IpmiInterface(
- node.get('fencer-ip'),
- node.get('fencer-user'),
- node.get('fencer-password'),
- verbose=CONF.debug)
-
- def force_shutdown(self):
- try:
- self.interface.power_down()
- except Exception as e:
- LOG.error(e)
-
- def graceful_shutdown(self):
- try:
- self.interface.power_soft()
- except Exception as e:
- LOG.error(e)
-
- def status(self):
- return self.interface.get_power_status()
-
- # @todo remove this fn as it's for testing purposes only :)
- def power_on(self):
- self.interface.power_on()
-
- def get_node_details(self, node):
- """Loads the node's fencing information from ``credentials_file``
- :param node: a dict contains node ip or hostname
- :return: a dict contains node fencing information
- """
- node_details = self.parser.find_server_by_ip(node.get('ip')) or \
- self.parser.find_server_by_hostname(node.get('host'))
-
- return node_details
-
- def fence(self):
- """Implements the fencing procedure for server fencing using ipmi
- :return: a list of nodes and weather they're fenced or not!
- """
- fenced_nodes = []
- for node in self.nodes:
- LOG.debug("fencing node {0}".format(node))
- # load node details
- node_details = self.get_node_details(node)
- # loop on the node number of n times trying to fence it gently,
- # if not force it!
- self.prepare_node(node_details)
- for retry in range(0, self.fencer_conf['retries']):
- if self.status():
- try:
- self.graceful_shutdown()
- except Exception as e:
- LOG.debug(e)
- else:
- node['status'] = True
- break
- time.sleep(self.fencer_conf['hold_period'])
- LOG.info('wait for %d seconds before retrying to gracefully '
- 'shutdown' % self.fencer_conf['hold_period'])
-
- try:
- self.force_shutdown()
- except Exception as e:
- LOG.error(e)
-
- if not self.status():
- node['status'] = True
- else:
- node['status'] = False
- fenced_nodes.append(node)
-
- return fenced_nodes
-
- def get_info(self):
- return {
- 'name': 'IPMI Interface driver',
- 'version': 1.1,
- 'author': 'Hewlett-Packard Enterprise Company, L.P'
- }
diff --git a/freezer_dr/fencers/drivers/ipmi/ipmitool.py b/freezer_dr/fencers/drivers/ipmi/ipmitool.py
deleted file mode 100644
index 8533765..0000000
--- a/freezer_dr/fencers/drivers/ipmi/ipmitool.py
+++ /dev/null
@@ -1,150 +0,0 @@
-# (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 distutils import spawn
-from oslo_log import log
-
-import subprocess
-import sys
-
-
-LOG = log.getLogger(__name__)
-
-
-class IpmiInterface:
-
- _IPMI = 'ipmitool'
- _RAW_CMD = '{0} -I {1} -H {2} -U {3} -P {4} '
- _SUPPORTED_INTERFACES = ['lan', 'lanplus']
-
- def __init__(self, host, username, password, verbose=False,
- interface='lanplus'):
- self._IPMI = spawn.find_executable('ipmitool')
- if not self._IPMI:
- self._IPMI = spawn.find_executable('ipmitool',
- path=':'.join(sys.path))
- if interface not in self._SUPPORTED_INTERFACES:
- raise Exception("Provided Interface is not supported")
-
- self._host = host
- self._username = username
- self._password = password
- self._verbose = verbose
- self._interface = interface
-
- self._update_cmd_credentials(
- host=host,
- username=username,
- password=password,
- interface=interface
- )
- LOG.debug('IPMI Interface initialized')
-
- def _update_cmd_credentials(self, host, username, password, interface):
- """
- Update credentials to work with different server
- :param host: IPMI IP address of the server
- :param username: IPMI username
- :param password: IPMI password
- :param interface: IPMI Interface lan, lanplus
- """
- cmd = self._RAW_CMD.format(
- self._IPMI,
- interface,
- host,
- username,
- password
- )
- self._cmd = cmd
-
- def get_power_status(self):
- """
- get the machine power status
- :return: 1 if the power is on and 0 if the power is off. otherwise it
- will return -1 for unknown state
- """
- cmd = self._cmd + ' chassis power status'
- output = self._process_request(cmd)
- if self._verbose:
- LOG.debug(output)
- if 'is on'.lower() in output.lower():
- return 1
- elif 'is off'.lower() in output.lower():
- return 0
- return -1 # power status unknown
-
- def power_down(self):
- """
- Force shutdown the machine
- """
- cmd = self._cmd + ' chassis power down'
- output = self._process_request(cmd)
- LOG.info('IPMI interface force shutdown node: %s, output: %s' %
- (self._host, output))
- return output
-
- def power_soft(self):
- """
- Softly shutdown the machine
- """
- cmd = self._cmd + 'chassis power soft'
- output = self._process_request(cmd)
- LOG.info('IPMI interface soft shutdown node: %s, output: %s' %
- (self._host, output))
- return output
-
- def power_reset(self):
- """
- restart the machine
- """
- cmd = self._cmd + ' chassis power reset'
- return self._process_request(cmd)
-
- def power_on(self):
- """
- power on the machine
- """
- cmd = self._cmd + ' chassis power on'
- return self._process_request(cmd)
-
- def _process_request(self, cmd):
- if self._verbose:
- LOG.debug('Executing IPMI command:', cmd)
-
- process = subprocess.Popen(cmd, shell=True,
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE)
- output, error = process.communicate()
-
- if self._verbose:
- LOG.debug('IPMI Output: ', output)
- LOG.debug('IPMI Error', error)
-
- if process.returncode:
- LOG.error(cmd)
- raise Exception(error)
- return output
-
- def _custom_cmd(self, cmd):
- """
- execute custom ipmitool commands
- :param cmd: string contains the command, for credentials and interface
- you should _update_cmd_credentials to update them first
- :return: output of the command you sent or raise error
- """
- cmd = self._cmd + cmd
- output = self._process_request(cmd)
- LOG.info('Executing IPMI custom command: %s with output: %s' %
- (cmd, output))
- return output
diff --git a/freezer_dr/fencers/drivers/libvirt/__init__.py b/freezer_dr/fencers/drivers/libvirt/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/freezer_dr/fencers/drivers/libvirt/driver.py b/freezer_dr/fencers/drivers/libvirt/driver.py
deleted file mode 100644
index 2749357..0000000
--- a/freezer_dr/fencers/drivers/libvirt/driver.py
+++ /dev/null
@@ -1,100 +0,0 @@
-# (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 freezer_dr.common.yaml_parser import YamlParser
-from freezer_dr.fencers.common.driver import FencerBaseDriver
-import libvirt
-from oslo_config import cfg
-from oslo_log import log
-import time
-
-CONF = cfg.CONF
-LOG = log.getLogger(__name__)
-
-
-class LibvirtDriver(FencerBaseDriver):
-
- def __init__(self, nodes, fencer_conf):
- super(LibvirtDriver, self).__init__(nodes, fencer_conf)
- self.parser = YamlParser(self.fencer_conf['credentials_file'])
- # initiate libvirt connection
- conn_name = self.fencer_conf.get('name', None)
- self.connection = libvirt.open(name=conn_name)
-
- def force_shutdown(self, node):
- target = self.connection.lookupByName(name=node.get('domain-name'))
- return target.destroy()
-
- def graceful_shutdown(self, node):
- target = self.connection.lookupByName(name=node.get('domain-name'))
- return target.shutdown()
-
- def status(self, node):
- target = self.connection.lookupByName(name=node.get('domain-name'))
- return target.isActive()
-
- def get_node_details(self, node):
- """Loads the node's fencing information from ``credentials_file``
- :param node: a dict contains node ip or hostname
- :return: a dict contains node fencing information
- """
- node_details = self.parser.find_server_by_ip(node.get('ip')) or \
- self.parser.find_server_by_hostname(node.get('host'))
-
- return node_details
-
- def fence(self):
- """Implements the fencing procedure for server fencing using ipmi
- :return: a list of nodes and weather they're fenced or not!
- """
- fenced_nodes = []
- for node in self.nodes:
- LOG.debug("fencing node {0}".format(node))
- # load node details
- node_details = self.get_node_details(node)
- # loop on the node number of n times trying to fence it gently,
- # if not force it!
- for retry in range(0, self.fencer_conf['retries']):
- if self.status(node=node_details):
- try:
- self.graceful_shutdown(node=node_details)
- except Exception as e:
- LOG.debug(e)
- else:
- node['status'] = True
- break
- time.sleep(self.fencer_conf['hold_period'])
- LOG.info('wait for %d seconds before retrying to gracefully '
- 'shutdown' % self.fencer_conf['hold_period'])
-
- try:
- self.force_shutdown(node=node_details)
- except Exception as e:
- LOG.error(e)
-
- if not self.status(node=node_details):
- node['status'] = True
- else:
- node['status'] = False
- fenced_nodes.append(node)
-
- return fenced_nodes
-
- def get_info(self):
- return {
- 'name': 'Libvirt Interface driver',
- 'version': 1.1,
- 'author': 'Hewlett-Packard Enterprise Company, L.P'
- }
-
-
diff --git a/freezer_dr/main.py b/freezer_dr/main.py
deleted file mode 100644
index 278c8ca..0000000
--- a/freezer_dr/main.py
+++ /dev/null
@@ -1,57 +0,0 @@
-# (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 __future__ import print_function
-
-from freezer_dr.common import config
-from freezer_dr.evacuators.common.manager import EvacuationManager
-from freezer_dr.monitors.common.manager import MonitorManager
-from freezer_dr.notifiers.common.manager import NotificationManager
-from oslo_config import cfg
-from oslo_log import log
-
-
-CONF = cfg.CONF
-LOG = log.getLogger(__name__)
-
-
-def main():
- config.configure()
- config.setup_logging()
- LOG.info('Starting Freezer DR ... ')
- # initialize the notification driver as it will be used in many parts
- notifier = NotificationManager()
- # load and initialize the monitoring driver
- monitor = MonitorManager(notifier=notifier.get_driver())
- # Do the monitoring procedure
- # Monitor, analyse, nodes down ?, wait, double check ? evacuate ..
- nodes = monitor.monitor()
-
- if nodes:
- # @todo put node in maintenance mode :) Not working with virtual
- # deployments
- # Load Fence driver
- # Shutdown the node
- evac = EvacuationManager()
- notify_nodes = evac.get_nodes_details(nodes)
- notifier.notify(notify_nodes, 'original')
- evacuated_nodes, failed_nodes = evac.evacuate(nodes)
- LOG.debug("Successfully evacuated nodes {0}".format(evacuated_nodes))
- LOG.debug("Failed to evacuate nodes {0}".format(failed_nodes))
- evacuated_nodes = evac.get_nodes_details(evacuated_nodes)
- notifier.notify(evacuated_nodes, 'success')
- failed_nodes = evac.get_nodes_details(failed_nodes)
- notifier.notify(failed_nodes, 'error')
- else:
- print("No nodes reported to be down")
diff --git a/freezer_dr/monitors/__init__.py b/freezer_dr/monitors/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/freezer_dr/monitors/common/__init__.py b/freezer_dr/monitors/common/__init__.py
deleted file mode 100644
index 8b13789..0000000
--- a/freezer_dr/monitors/common/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/freezer_dr/monitors/common/driver.py b/freezer_dr/monitors/common/driver.py
deleted file mode 100644
index 27bddc1..0000000
--- a/freezer_dr/monitors/common/driver.py
+++ /dev/null
@@ -1,101 +0,0 @@
-# (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
-
-from oslo_config import cfg
-
-CONF = cfg.CONF
-
-
-class MonitorBaseDriver(object, metaclass=abc.ABCMeta):
- """
- Abstract class that all monitoring plugins should implement to have a
- unified interface and as many plugins as we want...
- """
- _OPTS = []
-
- def __init__(self, backend_name, notifier):
- """
- 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/freezer/dr.conf
- :param backend_name: Name of section in the configuration file that
- contains your driver initialization details; like username, password,
- endpoint and so on. Variables in this section depends on your driver
-
- :param notifier: Notifier instance which can be used to notify the
- admins in case of error or problem happened during the DR process.
- You should only call notify method and send it your message to send
- it to the admins
- """
- CONF.register_opts(self._OPTS, group=backend_name)
- self.conf = CONF.get(backend_name)
- self.notifier = notifier
-
- @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/freezer_dr/monitors/common/manager.py b/freezer_dr/monitors/common/manager.py
deleted file mode 100644
index 84b3f57..0000000
--- a/freezer_dr/monitors/common/manager.py
+++ /dev/null
@@ -1,65 +0,0 @@
-# (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, notifier):
- monitor = CONF.get('monitoring')
- backend_name = monitor['backend_name']
- self.driver = importutils.import_object(
- monitor.driver,
- backend_name=backend_name,
- notifier=notifier
- )
- 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')
- 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/freezer_dr/monitors/drivers/__init__.py b/freezer_dr/monitors/drivers/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/freezer_dr/monitors/drivers/default/__init__.py b/freezer_dr/monitors/drivers/default/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/freezer_dr/monitors/drivers/default/driver.py b/freezer_dr/monitors/drivers/default/driver.py
deleted file mode 100644
index a885a41..0000000
--- a/freezer_dr/monitors/drivers/default/driver.py
+++ /dev/null
@@ -1,158 +0,0 @@
-# (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 freezer_dr.common.osclient import OSClient
-from freezer_dr.monitors.common.driver import MonitorBaseDriver
-
-from http.client import HTTPConnection
-from http.client import HTTPSConnection
-from http.client import socket
-
-from oslo_config import cfg
-from oslo_log import log
-from time import sleep
-
-from urllib.parse import urlparse
-
-CONF = cfg.CONF
-LOG = log.getLogger(__name__)
-
-
-class StandardDriver(MonitorBaseDriver):
- _OPTS = [
- cfg.StrOpt('username',
- help='username to be used to initialize the default '
- 'monitoring driver'),
- cfg.StrOpt('password',
- help='Password to be used for initializing the default '
- '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. should be provided in'
- ' key:value format'),
- ]
-
- def __init__(self, backend_name, notifier):
- super(StandardDriver, self).__init__(backend_name=backend_name,
- notifier=notifier)
- self.endpoint = self.conf.endpoint
- client = OSClient(
- authurl=self.conf.endpoint,
- username=self.conf.username,
- password=self.conf.password,
- **self.conf.kwargs
- )
- LOG.info("OSClient:: username: %s, password: %s, endpoint: %s, kwargs:"
- " %s" % (self.conf.username, '****', self.conf.endpoint,
- self.conf.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)
- if url.scheme == 'https':
- http_connector = HTTPSConnection
- else:
- http_connector = HTTPConnection
- try:
- connection = http_connector(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': 'Freezer DR 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/freezer_dr/monitors/drivers/dummy/__init__.py b/freezer_dr/monitors/drivers/dummy/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/freezer_dr/monitors/drivers/dummy/driver.py b/freezer_dr/monitors/drivers/dummy/driver.py
deleted file mode 100644
index 1915863..0000000
--- a/freezer_dr/monitors/drivers/dummy/driver.py
+++ /dev/null
@@ -1,64 +0,0 @@
-# (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 freezer_dr.monitors.common.driver import MonitorBaseDriver
-
-CONF = cfg.CONF
-
-
-class DummyDriver(MonitorBaseDriver):
- """A monitoring driver that returns a configured list of nodes as failed.
-
- This can be useful for testing without actually shutting down the nodes.
- The nodes that should be reported as failing, can be configured in the
- monitoring section of the freezer_dr configuration file as follows:
- kwargs = nodes_down:hostname1;hostname2
- """
- _OPTS = [
- cfg.ListOpt('nodes_down',
- default=[],
- required=True,
- help="fake list of failed compute nodes.")
- ]
-
- def __init__(self, backend_name, notifier):
- super(DummyDriver, self).__init__(backend_name=backend_name,
- notifier=notifier)
-
- hostnames = self.conf.get('nodes_down', [])
- self.nodes_down = [{'host': n} for n in hostnames]
-
- def get_data(self):
- return self.nodes_down
-
- def get_metrics(self):
- raise NotImplementedError()
-
- def process_failed(self, nodes=None, wait=0):
- return nodes
-
- def analyze_nodes(self, nodes):
- return nodes
-
- def is_alive(self):
- return True
-
- def get_info(self):
- return {
- 'name': 'Freezer DR Dummy Driver',
- 'version': 1.0,
- 'author': 'Hewlett-Packard Enterprise Development, L.P'
- }
diff --git a/freezer_dr/monitors/drivers/monasca/__init__.py b/freezer_dr/monitors/drivers/monasca/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/freezer_dr/monitors/drivers/monasca/driver.py b/freezer_dr/monitors/drivers/monasca/driver.py
deleted file mode 100644
index 329a92c..0000000
--- a/freezer_dr/monitors/drivers/monasca/driver.py
+++ /dev/null
@@ -1,339 +0,0 @@
-# (c) Copyright 2016 Hewlett-Packard Development Enterprise, 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 http.client
-import time
-import urllib.parse
-
-from monascaclient import client
-from oslo_config import cfg
-from oslo_log import log
-
-from freezer_dr.common import utils
-from freezer_dr.monitors.common import driver
-
-CONF = cfg.CONF
-LOG = log.getLogger(__name__)
-
-
-class MonascaDriver(driver.MonitorBaseDriver):
- """Monasca monitoring driver to monitor compute nodes. It makes use of
- Monasca to monitor the compute nodes. Metric information
- needed. 'hostname' must be used in dimensions to filter the
- values in alarms. You need to define alarms for all hosts with
- the required metrics.
- """
-
- _OPTS = [
- cfg.StrOpt('keystone_url',
- help="Keystone Url for authentication",
- required=True),
- cfg.StrOpt('username',
- help="cloud user used to record monasca alerts and alarms",
- required=True),
- cfg.StrOpt('password',
- help="Cloud user's password",
- required=True),
- cfg.StrOpt('project_name',
- help='Project/Tenant name. Default is admin',
- default='admin',
- required=True),
- cfg.BoolOpt('insecure',
- help='Use insecure connection.',
- default=False),
- cfg.StrOpt('project_domain_id',
- help="Project Domain Id. Default is default",
- default='default'),
- cfg.StrOpt('user_domain_id',
- help="User Domain Id. Default is default",
- default='default'),
- cfg.StrOpt('cacert',
- help='CA certificate. Default is None',
- default=None),
- cfg.StrOpt('monasca_url',
- help='Monasca endpoint URL. This is required to create a '
- 'monasca client instance '
- ),
- cfg.ListOpt('metrics',
- help='Monasca Metrics that needs to be checked. Each metric'
- ' should be defined in a seperate section in the'
- ' configuration file.',
- default=['host_alive_status'],
- required=True
- ),
- cfg.StrOpt('aggregate',
- choices=['any', 'all'],
- default='all',
- help="If more than one metric used and they reported "
- "different states i.e.(a:failed, b:success) should we "
- "evacuate the compute host if only one metric failed "
- "(any) or only if all failed we evacuate (all). "
- "Default is all")
- ]
-
- def __init__(self, backend_name, notifier):
- super(MonascaDriver, self).__init__(backend_name=backend_name,
- notifier=notifier)
- self.monasca_client = client.Client(
- "2_0",
- self.conf['monasca_url'],
- auth_url=self.conf['keystone_url'],
- username=self.conf['username'],
- password=self.conf['password'],
- project_name=self.conf['project_name'],
- user_doamin_id=self.conf['user_domain_id'],
- project_doamin_id=self.conf['project_domain_id'],
- insecure=self.conf.get('insecure'),
- cacert=self.conf.get('cacert', None)
- )
- # Compute nodes might be disabled or set to maintenance mode so
- # freezer-dr needs to process only enabled nodes ...
- self.nodes = [node for node in self.get_compute_nodes()
- if node['status'] == "enabled"]
- # register metric options in their groups and load their values
- self.__load_metrics()
-
- def _get_raw_data(self):
- """ This function returns the raw data we got from Monasca before
- processing and normalizing. You shouldn't call this function directly.
- :return: dict contains:
- {
- hostname1: {
- metric_name1: [{metric value 1}, {metric value 2}]
- metric_name2: [{metric value 1}, {metric value 2}]
- },
- hostname2: {
- metric_name1: [{metric value 1}, {metric value 2}]
- metric_name2: [{metric value 1}, {metric value 2}]
- }
- }
- """
- data = {}
- for node in self.nodes:
- data[node['host']] = {}
- for metric in self.conf.metrics:
- data[node['host']][metric] = self.monasca_client.alarms.list(
- **self._build_metrics(
- metric=metric,
- hostname=node['host']
- )
- )
- return data
-
- def get_data(self):
- """This function returns monitoring data from Monasca. It calls
- _get_raw_data to get raw data and then process these data returns
- a normalized dict
- :return: dict contains::
-
- {
- hostname1: {
- metric_name1: ['Ok', 'ALARM', 'UNDETERMINED']
- metric_name2: ['OK', 'OK', 'OK']
- },
- hostname2: {
- metric_name1: ['Ok', 'ALARM', 'OK']
- metric_name2: ['ALARM', 'UNDETERMINED', 'OK']
- }
- }
- """
- data = self._get_raw_data()
- data2 = {}
- for host, metric_results in data.items():
- data2[host] = {}
- for metric_name, metric_values in metric_results.iteritems():
- data2[host][metric_name] = []
- for metric_value in metric_values:
- data2[host][metric_name].append(metric_value.get('state'))
- return data2
-
- def process_failed(self, nodes=None, wait=1):
- time.sleep(wait)
- 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):
- """Lists all metrics
- :return: List of Metrics
- """
- return self.conf['metrics']
-
- def _build_metrics(self, metric, hostname=None):
- """Build the query to send to Monasca"""
- metric = CONF[metric]
- dimensions = {'hostname': hostname}
- dimensions.update(metric.get('dimensions', {}))
-
- fields = {
- 'metric_dimensions': dimensions,
- 'metric_name': metric['metric_name']
- }
- return fields
-
- def analyze_nodes(self, nodes):
- """It will check if the nodes are in 'OK' state or not. If not they
- will considered down. We have three states as follow:
- 1. OK
- 2. ALARM
- 3. UNDEFINED
- """
- # @todo(szaher) use list comprehension instead of loops
- # list below is correct and should return the extact same value like
- # the two nested for loops
- # nodes_down = [
- # {"host": hostname} for hostname, metrics in nodes.iteritems() if
- # [True for name, values in metrics.iteritems() if 'ALARM' in values]
- # ]
- nodes_data = []
- for node, metrics in nodes.iteritems():
- node_data = {node: []}
- for metric_name, metric_data in metrics.iteritems():
- node_data[node].append(
- self.__process_metric(node, metric_name, metric_data)
- )
- nodes_data.append(node_data)
-
- aggregate = self.conf.get('aggregate', 'all')
- aggregate += '({0})'
- nodes_down = []
- for node_data in nodes_data:
- node_info = {}
- for node, data in node_data.iteritems():
- if not data:
- LOG.warning('No data available for node: {0}'.format(node))
- continue
- node_info[node] = eval(aggregate.format(data))
- if node_info:
- nodes_down.append(node_info)
-
- if not nodes_down:
- return []
- return [
- {'host': host.keys()[0]} for host in nodes_down
- if True in host.values()
- ]
-
- def __process_metric(self, node, metric_name, metric_data):
- """Process metric values got from Monasca.
- Handles UNDETERMINED states and changes it to required state(read
- from config file).
- If no metric data found,"""
- metric_conf = CONF[metric_name]
- # process UNDETERMINED State and change it to the required state
- metric_data = [
- i if i in ['OK', 'ALARM'] else
- metric_conf.get('undetermined', 'ALARM').upper()
- for i in metric_data
- ]
- if not metric_data:
- message = """
- No data found for this metric: {0}