aboutsummaryrefslogtreecommitdiffstats
path: root/deploy/ansible_plugins
diff options
context:
space:
mode:
authorHarry Huang <huangxiangyu5@huawei.com>2018-02-01 15:56:03 +0800
committerHarry Huang <huangxiangyu5@huawei.com>2018-02-01 19:14:35 +0800
commit5e02737fa5f23451bbb443bbf9d67cbac603dc46 (patch)
treeb642b808ea19d13b8e7c370bafe4b431e4b7b560 /deploy/ansible_plugins
parentbce23c34f221cdd9a41608c6505b359d99963afe (diff)
Ansible callback support both V2.3 and V2.4
JIRA: COMPASS-585 1. Add conditional branch to support both ansible versions 2. Keep callback in ansible_plugins directory which will contains all plugins upcoming in the future Change-Id: I11f3d9700e07ad5bd7f03ad56f4e0bc061df05ec Signed-off-by: Harry Huang <huangxiangyu5@huawei.com>
Diffstat (limited to 'deploy/ansible_plugins')
-rw-r--r--deploy/ansible_plugins/callback/playbook_done.py111
-rw-r--r--deploy/ansible_plugins/callback/status_callback.py203
2 files changed, 314 insertions, 0 deletions
diff --git a/deploy/ansible_plugins/callback/playbook_done.py b/deploy/ansible_plugins/callback/playbook_done.py
new file mode 100644
index 00000000..4784ff63
--- /dev/null
+++ b/deploy/ansible_plugins/callback/playbook_done.py
@@ -0,0 +1,111 @@
+#!/usr/bin/env python
+#
+# Copyright 2014 Huawei Technologies Co. Ltd
+#
+# 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.
+
+"""Ansible playbook callback after a playbook run has completed."""
+import sys
+
+from distutils.version import LooseVersion
+from ansible import __version__ as __ansible_version__
+from ansible.plugins.callback import CallbackBase
+
+compass_bin = "/opt/compass/bin"
+sys.path.append(compass_bin)
+
+import switch_virtualenv # noqa: F401
+
+from compass.apiclient.restful import Client # noqa: E402
+from compass.utils import flags # noqa: E402
+
+
+flags.add('compass_server',
+ help='compass server url',
+ default='http://compass-deck/api')
+flags.add('compass_user_email',
+ help='compass user email',
+ default='admin@huawei.com')
+flags.add('compass_user_password',
+ help='compass user password',
+ default='admin')
+
+
+class CallbackModule(CallbackBase):
+ CALLBACK_VERSION = 2.0
+ CALLBACK_TYPE = 'notification'
+ CALLBACK_NAME = 'playbook_done'
+ CALLBACK_NEEDS_WHITELIST = True
+
+ def __init__(self):
+ super(CallbackModule, self).__init__()
+
+ self.play = None
+ self.loader = None
+ self.disabled = False
+ try:
+ self.client = self._get_client()
+ except Exception:
+ self.disabled = True
+ self._display.error("No compass server found"
+ "disabling this plugin")
+
+ def _get_client(self):
+ return Client(flags.OPTIONS.compass_server)
+
+ def _login(self, client):
+ """get apiclient token."""
+ status, resp = client.get_token(
+ flags.OPTIONS.compass_user_email,
+ flags.OPTIONS.compass_user_password
+ )
+ self._display.warning(
+ 'login status: %s, resp: %s' %
+ (status, resp)
+ )
+ if status >= 400:
+ raise Exception(
+ 'failed to login %s with user %s',
+ flags.OPTIONS.compass_server,
+ flags.OPTIONS.compass_user_email
+ )
+ return resp['token']
+
+ def v2_playbook_on_play_start(self, play):
+ self.play = play
+ self.loader = self.play.get_loader()
+ return
+
+ def v2_playbook_on_stats(self, stats):
+ if LooseVersion(__ansible_version__) < LooseVersion("2.4"):
+ all_vars = self.play.get_variable_manager().get_vars(self.loader)
+ else:
+ all_vars = self.play.get_variable_manager().get_vars()
+ host_vars = all_vars["hostvars"]
+ hosts = sorted(stats.processed.keys())
+ cluster_name = host_vars[hosts[0]]['cluster_name']
+ self._display.warning("cluster_name %s" % cluster_name)
+
+ failures = False
+ unreachable = False
+
+ for host in hosts:
+ summary = stats.summarize(host)
+
+ if summary['failures'] > 0:
+ failures = True
+ if summary['unreachable'] > 0:
+ unreachable = True
+
+ if failures or unreachable:
+ return
diff --git a/deploy/ansible_plugins/callback/status_callback.py b/deploy/ansible_plugins/callback/status_callback.py
new file mode 100644
index 00000000..b87d2094
--- /dev/null
+++ b/deploy/ansible_plugins/callback/status_callback.py
@@ -0,0 +1,203 @@
+##############################################################################
+# Copyright (c) 2016 HUAWEI TECHNOLOGIES CO.,LTD and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+import httplib
+import simplejson as json
+import sys # noqa:F401
+
+from distutils.version import LooseVersion
+from ansible import __version__ as __ansible_version__
+from ansible.plugins.callback import CallbackBase
+
+COMPASS_HOST = "compass-deck"
+
+
+# def task_error(display, host, data):
+# display.display("task_error: host=%s,data=%s" % (host, data))
+#
+# if isinstance(data, dict):
+# invocation = data.pop('invocation', {})
+#
+# notify_host(display, COMPASS_HOST, host, "failed")
+
+
+class CallbackModule(CallbackBase):
+ """
+ logs playbook results, per host, in /var/log/ansible/hosts
+ """
+ CALLBACK_VERSION = 2.0
+ CALLBACK_TYPE = 'notification'
+ CALLBACK_NAME = 'status_callback'
+ CALLBACK_NEEDS_WHITELIST = True
+
+ def __init__(self):
+ super(CallbackModule, self).__init__()
+
+ def v2_on_any(self, *args, **kwargs):
+ pass
+
+ def v2_runner_on_failed(self, res, ignore_errors=False):
+ # task_error(self._display, host, res)
+ pass
+
+ def v2_runner_on_ok(self, res):
+ pass
+
+ def v2_runner_on_skipped(self, host, item=None):
+ pass
+
+ def v2_runner_on_unreachable(self, host, res):
+ pass
+
+ def v2_runner_on_no_hosts(self):
+ pass
+
+ def v2_runner_on_async_poll(self, host, res, jid, clock):
+ pass
+
+ def v2_runner_on_async_ok(self, host, res, jid):
+ pass
+
+ def v2_runner_on_async_failed(self, host, res, jid):
+ # task_error(self._display, host, res)
+ pass
+
+ def v2_playbook_on_start(self):
+ pass
+
+ def v2_playbook_on_notify(self, host, handler):
+ pass
+
+ def v2_playbook_on_no_hosts_matched(self):
+ pass
+
+ def v2_playbook_on_no_hosts_remaining(self):
+ pass
+
+ def v2_playbook_on_task_start(self, name, is_conditional):
+ pass
+
+ def v2_playbook_on_vars_prompt(self, varname, private=True, prompt=None,
+ encrypt=None, confirm=False, salt_size=None, salt=None, default=None): # noqa
+ pass
+
+ def v2_playbook_on_setup(self):
+ pass
+
+ def v2_playbook_on_import_for_host(self, host, imported_file):
+ pass
+
+ def v2_playbook_on_not_import_for_host(self, host, missing_file):
+ pass
+
+ def v2_playbook_on_play_start(self, play):
+ self.play = play
+ self.loader = self.play.get_loader()
+ return
+
+ def v2_playbook_on_stats(self, stats):
+ self._display.display("playbook_on_stats enter")
+ if LooseVersion(__ansible_version__) < LooseVersion("2.4"):
+ all_vars = self.play.get_variable_manager().get_vars(self.loader)
+ else:
+ all_vars = self.play.get_variable_manager().get_vars()
+ host_vars = all_vars["hostvars"]
+ hosts = sorted(stats.processed.keys())
+ cluster_name = host_vars[hosts[0]]['cluster_name']
+
+ headers = {"Content-type": "application/json",
+ "Accept": "*/*"}
+ conn = httplib.HTTPConnection(COMPASS_HOST, 80)
+ token = auth(conn)
+ headers["X-Auth-Token"] = token
+ get_url = "/api/clusterhosts"
+ conn.request("GET", get_url, "", headers)
+ resp = conn.getresponse()
+ raise_for_status(resp)
+ clusterhost_data = json.loads(resp.read())
+ clusterhost_mapping = {}
+ for item in clusterhost_data:
+ if item["clustername"] == cluster_name:
+ clusterhost_mapping.update({item["hostname"]:
+ item["clusterhost_id"]})
+
+ force_error = False
+ if "localhost" in hosts:
+ summary = stats.summarize("localhost")
+ if summary['failures'] > 0 or summary['unreachable'] > 0:
+ force_error = True
+
+ for hostname, hostid in clusterhost_mapping.iteritems():
+ if hostname not in hosts:
+ continue
+
+ summary = stats.summarize(hostname)
+ # self._display.display("host: %s \nsummary: %s\n" % (host, summary)) # noqa
+
+ if summary['failures'] > 0 or summary['unreachable'] > 0 \
+ or force_error:
+ status = "error"
+ else:
+ status = "succ"
+ self._display.display("hostname: %s" % hostname)
+ notify_host(self._display, COMPASS_HOST, hostid, status)
+
+
+def raise_for_status(resp):
+ if resp.status < 200 or resp.status > 300:
+ raise RuntimeError(
+ "%s, %s, %s" %
+ (resp.status, resp.reason, resp.read()))
+
+
+def auth(conn):
+ credential = {}
+ credential['email'] = "admin@huawei.com"
+ credential['password'] = "admin"
+ url = "/api/users/token"
+ headers = {"Content-type": "application/json",
+ "Accept": "*/*"}
+ conn.request("POST", url, json.dumps(credential), headers)
+ resp = conn.getresponse()
+
+ raise_for_status(resp)
+ return json.loads(resp.read())["token"]
+
+
+def notify_host(display, compass_host, hostid, status):
+ url = "/api/clusterhosts/%s/state" % hostid
+ if status == "succ":
+ body = {"state": "SUCCESSFUL"}
+ elif status == "error":
+ body = {"state": "ERROR"}
+ else:
+ display.error("notify_host: hostid %s with status %s is not supported"
+ % (hostid, status))
+ return
+
+ headers = {"Content-type": "application/json",
+ "Accept": "*/*"}
+
+ conn = httplib.HTTPConnection(compass_host, 80)
+ token = auth(conn)
+ headers["X-Auth-Token"] = token
+ display.display("host=%s,url=%s,body=%s,headers=%s" %
+ (compass_host, url, json.dumps(body), headers))
+ conn.request("POST", url, json.dumps(body), headers)
+ resp = conn.getresponse()
+ try:
+ raise_for_status(resp)
+ display.display(
+ "notify host status success!!! status=%s, body=%s" %
+ (resp.status, resp.read()))
+ except Exception as e:
+ display.error("http request failed %s" % str(e))
+ raise
+ finally:
+ conn.close()