|
|
05afe3 |
From 13ae97dec5754642af4d0d0edc03d9290e792e7f Mon Sep 17 00:00:00 2001
|
|
|
05afe3 |
From: Oyvind Albrigtsen <oalbrigt@redhat.com>
|
|
|
05afe3 |
Date: Thu, 19 Jul 2018 16:12:35 +0200
|
|
|
05afe3 |
Subject: [PATCH 1/5] Add Python library
|
|
|
05afe3 |
|
|
|
05afe3 |
---
|
|
|
05afe3 |
heartbeat/Makefile.am | 3 +-
|
|
|
05afe3 |
heartbeat/ocf.py | 136 ++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
05afe3 |
2 files changed, 138 insertions(+), 1 deletion(-)
|
|
|
05afe3 |
create mode 100644 heartbeat/ocf.py
|
|
|
05afe3 |
|
|
|
05afe3 |
diff --git a/heartbeat/Makefile.am b/heartbeat/Makefile.am
|
|
|
05afe3 |
index d4750bf09..1333f8feb 100644
|
|
|
05afe3 |
--- a/heartbeat/Makefile.am
|
|
|
05afe3 |
+++ b/heartbeat/Makefile.am
|
|
|
05afe3 |
@@ -185,7 +185,8 @@ ocfcommon_DATA = ocf-shellfuncs \
|
|
|
05afe3 |
ora-common.sh \
|
|
|
05afe3 |
mysql-common.sh \
|
|
|
05afe3 |
nfsserver-redhat.sh \
|
|
|
05afe3 |
- findif.sh
|
|
|
05afe3 |
+ findif.sh \
|
|
|
05afe3 |
+ ocf.py
|
|
|
05afe3 |
|
|
|
05afe3 |
# Legacy locations
|
|
|
05afe3 |
hbdir = $(sysconfdir)/ha.d
|
|
|
05afe3 |
diff --git a/heartbeat/ocf.py b/heartbeat/ocf.py
|
|
|
05afe3 |
new file mode 100644
|
|
|
05afe3 |
index 000000000..12be7a2a4
|
|
|
05afe3 |
--- /dev/null
|
|
|
05afe3 |
+++ b/heartbeat/ocf.py
|
|
|
05afe3 |
@@ -0,0 +1,136 @@
|
|
|
05afe3 |
+#
|
|
|
05afe3 |
+# Copyright (c) 2016 Red Hat, Inc, Oyvind Albrigtsen
|
|
|
05afe3 |
+# All Rights Reserved.
|
|
|
05afe3 |
+#
|
|
|
05afe3 |
+#
|
|
|
05afe3 |
+# This library is free software; you can redistribute it and/or
|
|
|
05afe3 |
+# modify it under the terms of the GNU Lesser General Public
|
|
|
05afe3 |
+# License as published by the Free Software Foundation; either
|
|
|
05afe3 |
+# version 2.1 of the License, or (at your option) any later version.
|
|
|
05afe3 |
+#
|
|
|
05afe3 |
+# This library is distributed in the hope that it will be useful,
|
|
|
05afe3 |
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
05afe3 |
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
05afe3 |
+# Lesser General Public License for more details.
|
|
|
05afe3 |
+#
|
|
|
05afe3 |
+# You should have received a copy of the GNU Lesser General Public
|
|
|
05afe3 |
+# License along with this library; if not, write to the Free Software
|
|
|
05afe3 |
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
05afe3 |
+#
|
|
|
05afe3 |
+
|
|
|
05afe3 |
+import sys, os, logging, syslog
|
|
|
05afe3 |
+
|
|
|
05afe3 |
+argv=sys.argv
|
|
|
05afe3 |
+env=os.environ
|
|
|
05afe3 |
+
|
|
|
05afe3 |
+#
|
|
|
05afe3 |
+# Common variables for the OCF Resource Agents supplied by
|
|
|
05afe3 |
+# heartbeat.
|
|
|
05afe3 |
+#
|
|
|
05afe3 |
+
|
|
|
05afe3 |
+OCF_SUCCESS=0
|
|
|
05afe3 |
+OCF_ERR_GENERIC=1
|
|
|
05afe3 |
+OCF_ERR_ARGS=2
|
|
|
05afe3 |
+OCF_ERR_UNIMPLEMENTED=3
|
|
|
05afe3 |
+OCF_ERR_PERM=4
|
|
|
05afe3 |
+OCF_ERR_INSTALLED=5
|
|
|
05afe3 |
+OCF_ERR_CONFIGURED=6
|
|
|
05afe3 |
+OCF_NOT_RUNNING=7
|
|
|
05afe3 |
+
|
|
|
05afe3 |
+# Non-standard values.
|
|
|
05afe3 |
+#
|
|
|
05afe3 |
+# OCF does not include the concept of master/slave resources so we
|
|
|
05afe3 |
+# need to extend it so we can discover a resource's complete state.
|
|
|
05afe3 |
+#
|
|
|
05afe3 |
+# OCF_RUNNING_MASTER:
|
|
|
05afe3 |
+# The resource is in "master" mode and fully operational
|
|
|
05afe3 |
+# OCF_FAILED_MASTER:
|
|
|
05afe3 |
+# The resource is in "master" mode but in a failed state
|
|
|
05afe3 |
+#
|
|
|
05afe3 |
+# The extra two values should only be used during a probe.
|
|
|
05afe3 |
+#
|
|
|
05afe3 |
+# Probes are used to discover resources that were started outside of
|
|
|
05afe3 |
+# the CRM and/or left behind if the LRM fails.
|
|
|
05afe3 |
+#
|
|
|
05afe3 |
+# They can be identified in RA scripts by checking for:
|
|
|
05afe3 |
+# [ "${__OCF_ACTION}" = "monitor" -a "${OCF_RESKEY_CRM_meta_interval}" = "0" ]
|
|
|
05afe3 |
+#
|
|
|
05afe3 |
+# Failed "slaves" should continue to use: OCF_ERR_GENERIC
|
|
|
05afe3 |
+# Fully operational "slaves" should continue to use: OCF_SUCCESS
|
|
|
05afe3 |
+#
|
|
|
05afe3 |
+OCF_RUNNING_MASTER=8
|
|
|
05afe3 |
+OCF_FAILED_MASTER=9
|
|
|
05afe3 |
+
|
|
|
05afe3 |
+
|
|
|
05afe3 |
+## Own logger handler that uses old-style syslog handler as otherwise
|
|
|
05afe3 |
+## everything is sourced from /dev/syslog
|
|
|
05afe3 |
+class SyslogLibHandler(logging.StreamHandler):
|
|
|
05afe3 |
+ """
|
|
|
05afe3 |
+ A handler class that correctly push messages into syslog
|
|
|
05afe3 |
+ """
|
|
|
05afe3 |
+ def emit(self, record):
|
|
|
05afe3 |
+ syslog_level = {
|
|
|
05afe3 |
+ logging.CRITICAL:syslog.LOG_CRIT,
|
|
|
05afe3 |
+ logging.ERROR:syslog.LOG_ERR,
|
|
|
05afe3 |
+ logging.WARNING:syslog.LOG_WARNING,
|
|
|
05afe3 |
+ logging.INFO:syslog.LOG_INFO,
|
|
|
05afe3 |
+ logging.DEBUG:syslog.LOG_DEBUG,
|
|
|
05afe3 |
+ logging.NOTSET:syslog.LOG_DEBUG,
|
|
|
05afe3 |
+ }[record.levelno]
|
|
|
05afe3 |
+
|
|
|
05afe3 |
+ msg = self.format(record)
|
|
|
05afe3 |
+
|
|
|
05afe3 |
+ # syslog.syslog can not have 0x00 character inside or exception
|
|
|
05afe3 |
+ # is thrown
|
|
|
05afe3 |
+ syslog.syslog(syslog_level, msg.replace("\x00","\n"))
|
|
|
05afe3 |
+ return
|
|
|
05afe3 |
+
|
|
|
05afe3 |
+
|
|
|
05afe3 |
+OCF_RESOURCE_INSTANCE = env.get("OCF_RESOURCE_INSTANCE")
|
|
|
05afe3 |
+
|
|
|
05afe3 |
+HA_DEBUG = env.get("HA_debug", 0)
|
|
|
05afe3 |
+HA_DATEFMT = env.get("HA_DATEFMT", "%b %d %T ")
|
|
|
05afe3 |
+HA_LOGFACILITY = env.get("HA_LOGFACILITY")
|
|
|
05afe3 |
+HA_LOGFILE = env.get("HA_LOGFILE")
|
|
|
05afe3 |
+HA_DEBUGLOG = env.get("HA_DEBUGLOG")
|
|
|
05afe3 |
+
|
|
|
05afe3 |
+log = logging.getLogger(os.path.basename(argv[0]))
|
|
|
05afe3 |
+log.setLevel(logging.DEBUG)
|
|
|
05afe3 |
+
|
|
|
05afe3 |
+## add logging to stderr
|
|
|
05afe3 |
+if sys.stdout.isatty():
|
|
|
05afe3 |
+ seh = logging.StreamHandler(stream=sys.stderr)
|
|
|
05afe3 |
+ if HA_DEBUG == 0:
|
|
|
05afe3 |
+ seh.setLevel(logging.WARNING)
|
|
|
05afe3 |
+ sehformatter = logging.Formatter('%(filename)s(%(OCF_RESOURCE_INSTANCE)s)[%(process)s]:\t%(asctime)s%(levelname)s: %(message)s', datefmt=HA_DATEFMT)
|
|
|
05afe3 |
+ seh.setFormatter(sehformatter)
|
|
|
05afe3 |
+ log.addHandler(seh)
|
|
|
05afe3 |
+
|
|
|
05afe3 |
+## add logging to syslog
|
|
|
05afe3 |
+if HA_LOGFACILITY:
|
|
|
05afe3 |
+ slh = SyslogLibHandler()
|
|
|
05afe3 |
+ if HA_DEBUG == 0:
|
|
|
05afe3 |
+ slh.setLevel(logging.WARNING)
|
|
|
05afe3 |
+ slhformatter = logging.Formatter('%(levelname)s: %(message)s')
|
|
|
05afe3 |
+ slh.setFormatter(slhformatter)
|
|
|
05afe3 |
+ log.addHandler(slh)
|
|
|
05afe3 |
+
|
|
|
05afe3 |
+## add logging to file
|
|
|
05afe3 |
+if HA_LOGFILE:
|
|
|
05afe3 |
+ lfh = logging.FileHandler(HA_LOGFILE)
|
|
|
05afe3 |
+ if HA_DEBUG == 0:
|
|
|
05afe3 |
+ lfh.setLevel(logging.WARNING)
|
|
|
05afe3 |
+ lfhformatter = logging.Formatter('%(filename)s(%(OCF_RESOURCE_INSTANCE)s)[%(process)s]:\t%(asctime)s%(levelname)s: %(message)s', datefmt=HA_DATEFMT)
|
|
|
05afe3 |
+ lfh.setFormatter(lfhformatter)
|
|
|
05afe3 |
+ log.addHandler(lfh)
|
|
|
05afe3 |
+
|
|
|
05afe3 |
+## add debug logging to file
|
|
|
05afe3 |
+if HA_DEBUGLOG and HA_LOGFILE != HA_DEBUGLOG:
|
|
|
05afe3 |
+ dfh = logging.FileHandler(HA_DEBUGLOG)
|
|
|
05afe3 |
+ if HA_DEBUG == 0:
|
|
|
05afe3 |
+ dfh.setLevel(logging.WARNING)
|
|
|
05afe3 |
+ dfhformatter = logging.Formatter('%(filename)s(%(OCF_RESOURCE_INSTANCE)s)[%(process)s]:\t%(asctime)s%(levelname)s: %(message)s', datefmt=HA_DATEFMT)
|
|
|
05afe3 |
+ dfh.setFormatter(dfhformatter)
|
|
|
05afe3 |
+ log.addHandler(dfh)
|
|
|
05afe3 |
+
|
|
|
05afe3 |
+logger = logging.LoggerAdapter(log, {'OCF_RESOURCE_INSTANCE': OCF_RESOURCE_INSTANCE})
|
|
|
05afe3 |
|
|
|
05afe3 |
From 2ade8dbf1f6f6d3889dd1ddbf40858edf10fbdc7 Mon Sep 17 00:00:00 2001
|
|
|
05afe3 |
From: Oyvind Albrigtsen <oalbrigt@redhat.com>
|
|
|
05afe3 |
Date: Thu, 19 Jul 2018 16:20:39 +0200
|
|
|
05afe3 |
Subject: [PATCH 2/5] gcp-vpc-move-vip: use Python library
|
|
|
05afe3 |
|
|
|
05afe3 |
---
|
|
|
05afe3 |
heartbeat/gcp-vpc-move-vip.in | 42 +++++++++++++++++++++---------------------
|
|
|
05afe3 |
1 file changed, 21 insertions(+), 21 deletions(-)
|
|
|
05afe3 |
|
|
|
05afe3 |
diff --git a/heartbeat/gcp-vpc-move-vip.in b/heartbeat/gcp-vpc-move-vip.in
|
|
|
05afe3 |
index af2080502..eb5bce6a8 100755
|
|
|
05afe3 |
--- a/heartbeat/gcp-vpc-move-vip.in
|
|
|
05afe3 |
+++ b/heartbeat/gcp-vpc-move-vip.in
|
|
|
05afe3 |
@@ -22,6 +22,11 @@ import os
|
|
|
05afe3 |
import sys
|
|
|
05afe3 |
import time
|
|
|
05afe3 |
|
|
|
05afe3 |
+OCF_FUNCTIONS_DIR="%s/lib/heartbeat" % os.environ.get("OCF_ROOT")
|
|
|
05afe3 |
+sys.path.append(OCF_FUNCTIONS_DIR)
|
|
|
05afe3 |
+
|
|
|
05afe3 |
+from ocf import *
|
|
|
05afe3 |
+
|
|
|
05afe3 |
try:
|
|
|
05afe3 |
import googleapiclient.discovery
|
|
|
05afe3 |
except ImportError:
|
|
|
05afe3 |
@@ -40,10 +45,6 @@ else:
|
|
|
05afe3 |
CONN = None
|
|
|
05afe3 |
THIS_VM = None
|
|
|
05afe3 |
ALIAS = None
|
|
|
05afe3 |
-OCF_SUCCESS = 0
|
|
|
05afe3 |
-OCF_ERR_GENERIC = 1
|
|
|
05afe3 |
-OCF_ERR_CONFIGURED = 6
|
|
|
05afe3 |
-OCF_NOT_RUNNING = 7
|
|
|
05afe3 |
METADATA_SERVER = 'http://metadata.google.internal/computeMetadata/v1/'
|
|
|
05afe3 |
METADATA_HEADERS = {'Metadata-Flavor': 'Google'}
|
|
|
05afe3 |
METADATA = \
|
|
|
05afe3 |
@@ -206,11 +207,11 @@ def gcp_alias_start(alias):
|
|
|
05afe3 |
# If I already have the IP, exit. If it has an alias IP that isn't the VIP,
|
|
|
05afe3 |
# then remove it
|
|
|
05afe3 |
if my_alias == alias:
|
|
|
05afe3 |
- logging.info(
|
|
|
05afe3 |
+ logger.info(
|
|
|
05afe3 |
'%s already has %s attached. No action required' % (THIS_VM, alias))
|
|
|
05afe3 |
sys.exit(OCF_SUCCESS)
|
|
|
05afe3 |
elif my_alias:
|
|
|
05afe3 |
- logging.info('Removing %s from %s' % (my_alias, THIS_VM))
|
|
|
05afe3 |
+ logger.info('Removing %s from %s' % (my_alias, THIS_VM))
|
|
|
05afe3 |
set_alias(project, my_zone, THIS_VM, '')
|
|
|
05afe3 |
|
|
|
05afe3 |
# Loops through all hosts & remove the alias IP from the host that has it
|
|
|
05afe3 |
@@ -223,7 +224,7 @@ def gcp_alias_start(alias):
|
|
|
05afe3 |
host_zone = get_zone(project, host)
|
|
|
05afe3 |
host_alias = get_alias(project, host_zone, host)
|
|
|
05afe3 |
if alias == host_alias:
|
|
|
05afe3 |
- logging.info(
|
|
|
05afe3 |
+ logger.info(
|
|
|
05afe3 |
'%s is attached to %s - Removing all alias IP addresses from %s' %
|
|
|
05afe3 |
(alias, host, host))
|
|
|
05afe3 |
set_alias(project, host_zone, host, '')
|
|
|
05afe3 |
@@ -237,14 +238,14 @@ def gcp_alias_start(alias):
|
|
|
05afe3 |
# Check the IP has been added
|
|
|
05afe3 |
my_alias = get_localhost_alias()
|
|
|
05afe3 |
if alias == my_alias:
|
|
|
05afe3 |
- logging.info('Finished adding %s to %s' % (alias, THIS_VM))
|
|
|
05afe3 |
+ logger.info('Finished adding %s to %s' % (alias, THIS_VM))
|
|
|
05afe3 |
elif my_alias:
|
|
|
05afe3 |
- logging.error(
|
|
|
05afe3 |
+ logger.error(
|
|
|
05afe3 |
'Failed to add IP. %s has an IP attached but it isn\'t %s' %
|
|
|
05afe3 |
(THIS_VM, alias))
|
|
|
05afe3 |
sys.exit(OCF_ERR_GENERIC)
|
|
|
05afe3 |
else:
|
|
|
05afe3 |
- logging.error('Failed to add IP address %s to %s' % (alias, THIS_VM))
|
|
|
05afe3 |
+ logger.error('Failed to add IP address %s to %s' % (alias, THIS_VM))
|
|
|
05afe3 |
sys.exit(OCF_ERR_GENERIC)
|
|
|
05afe3 |
|
|
|
05afe3 |
|
|
|
05afe3 |
@@ -254,14 +255,14 @@ def gcp_alias_stop(alias):
|
|
|
05afe3 |
project = get_metadata('project/project-id')
|
|
|
05afe3 |
|
|
|
05afe3 |
if my_alias == alias:
|
|
|
05afe3 |
- logging.info('Removing %s from %s' % (my_alias, THIS_VM))
|
|
|
05afe3 |
+ logger.info('Removing %s from %s' % (my_alias, THIS_VM))
|
|
|
05afe3 |
set_alias(project, my_zone, THIS_VM, '')
|
|
|
05afe3 |
|
|
|
05afe3 |
|
|
|
05afe3 |
def gcp_alias_status(alias):
|
|
|
05afe3 |
my_alias = get_localhost_alias()
|
|
|
05afe3 |
if alias == my_alias:
|
|
|
05afe3 |
- logging.info('%s has the correct IP address attached' % THIS_VM)
|
|
|
05afe3 |
+ logger.info('%s has the correct IP address attached' % THIS_VM)
|
|
|
05afe3 |
else:
|
|
|
05afe3 |
sys.exit(OCF_NOT_RUNNING)
|
|
|
05afe3 |
|
|
|
05afe3 |
@@ -275,25 +276,24 @@ def validate():
|
|
|
05afe3 |
try:
|
|
|
05afe3 |
CONN = googleapiclient.discovery.build('compute', 'v1')
|
|
|
05afe3 |
except Exception as e:
|
|
|
05afe3 |
- logging.error('Couldn\'t connect with google api: ' + str(e))
|
|
|
05afe3 |
+ logger.error('Couldn\'t connect with google api: ' + str(e))
|
|
|
05afe3 |
sys.exit(OCF_ERR_CONFIGURED)
|
|
|
05afe3 |
|
|
|
05afe3 |
try:
|
|
|
05afe3 |
THIS_VM = get_metadata('instance/name')
|
|
|
05afe3 |
except Exception as e:
|
|
|
05afe3 |
- logging.error('Couldn\'t get instance name, is this running inside GCE?: ' + str(e))
|
|
|
05afe3 |
+ logger.error('Couldn\'t get instance name, is this running inside GCE?: ' + str(e))
|
|
|
05afe3 |
sys.exit(OCF_ERR_CONFIGURED)
|
|
|
05afe3 |
|
|
|
05afe3 |
ALIAS = os.environ.get('OCF_RESKEY_alias_ip')
|
|
|
05afe3 |
if not ALIAS:
|
|
|
05afe3 |
- logging.error('Missing alias_ip parameter')
|
|
|
05afe3 |
+ logger.error('Missing alias_ip parameter')
|
|
|
05afe3 |
sys.exit(OCF_ERR_CONFIGURED)
|
|
|
05afe3 |
|
|
|
05afe3 |
|
|
|
05afe3 |
def configure_logs():
|
|
|
05afe3 |
# Prepare logging
|
|
|
05afe3 |
- logging.basicConfig(
|
|
|
05afe3 |
- format='gcp:alias - %(levelname)s - %(message)s', level=logging.INFO)
|
|
|
05afe3 |
+ global logger
|
|
|
05afe3 |
logging.getLogger('googleapiclient').setLevel(logging.WARN)
|
|
|
05afe3 |
logging_env = os.environ.get('OCF_RESKEY_stackdriver_logging')
|
|
|
05afe3 |
if logging_env:
|
|
|
05afe3 |
@@ -307,10 +307,10 @@ def configure_logs():
|
|
|
05afe3 |
handler.setLevel(logging.INFO)
|
|
|
05afe3 |
formatter = logging.Formatter('gcp:alias "%(message)s"')
|
|
|
05afe3 |
handler.setFormatter(formatter)
|
|
|
05afe3 |
- root_logger = logging.getLogger()
|
|
|
05afe3 |
- root_logger.addHandler(handler)
|
|
|
05afe3 |
+ log.addHandler(handler)
|
|
|
05afe3 |
+ logger = logging.LoggerAdapter(log, {'OCF_RESOURCE_INSTANCE': OCF_RESOURCE_INSTANCE})
|
|
|
05afe3 |
except ImportError:
|
|
|
05afe3 |
- logging.error('Couldn\'t import google.cloud.logging, '
|
|
|
05afe3 |
+ logger.error('Couldn\'t import google.cloud.logging, '
|
|
|
05afe3 |
'disabling Stackdriver-logging support')
|
|
|
05afe3 |
|
|
|
05afe3 |
|
|
|
05afe3 |
@@ -331,7 +331,7 @@ def main():
|
|
|
05afe3 |
elif 'status' in sys.argv[1] or 'monitor' in sys.argv[1]:
|
|
|
05afe3 |
gcp_alias_status(ALIAS)
|
|
|
05afe3 |
else:
|
|
|
05afe3 |
- logging.error('no such function %s' % str(sys.argv[1]))
|
|
|
05afe3 |
+ logger.error('no such function %s' % str(sys.argv[1]))
|
|
|
05afe3 |
|
|
|
05afe3 |
|
|
|
05afe3 |
if __name__ == "__main__":
|
|
|
05afe3 |
|
|
|
05afe3 |
From 9e9ea17c42df27d4c13fed9badba295df48437f2 Mon Sep 17 00:00:00 2001
|
|
|
05afe3 |
From: Oyvind Albrigtsen <oalbrigt@redhat.com>
|
|
|
05afe3 |
Date: Fri, 20 Jul 2018 13:27:42 +0200
|
|
|
05afe3 |
Subject: [PATCH 3/5] gcp-vpc-move-vip: moved alias-parameters to top of
|
|
|
05afe3 |
metadata
|
|
|
05afe3 |
|
|
|
05afe3 |
---
|
|
|
05afe3 |
heartbeat/gcp-vpc-move-vip.in | 20 ++++++++++----------
|
|
|
05afe3 |
1 file changed, 10 insertions(+), 10 deletions(-)
|
|
|
05afe3 |
|
|
|
05afe3 |
diff --git a/heartbeat/gcp-vpc-move-vip.in b/heartbeat/gcp-vpc-move-vip.in
|
|
|
05afe3 |
index eb5bce6a8..ba61193b6 100755
|
|
|
05afe3 |
--- a/heartbeat/gcp-vpc-move-vip.in
|
|
|
05afe3 |
+++ b/heartbeat/gcp-vpc-move-vip.in
|
|
|
05afe3 |
@@ -55,6 +55,16 @@ METADATA = \
|
|
|
05afe3 |
<longdesc lang="en">Floating IP Address on Google Cloud Platform - Using Alias IP address functionality to attach a secondary IP address to a running instance</longdesc>
|
|
|
05afe3 |
<shortdesc lang="en">Floating IP Address on Google Cloud Platform</shortdesc>
|
|
|
05afe3 |
<parameters>
|
|
|
05afe3 |
+ <parameter name="alias_ip" unique="1" required="1">
|
|
|
05afe3 |
+ <longdesc lang="en">IP Address to be added including CIDR. E.g 192.168.0.1/32</longdesc>
|
|
|
05afe3 |
+ <shortdesc lang="en">IP Address to be added including CIDR. E.g 192.168.0.1/32</shortdesc>
|
|
|
05afe3 |
+ <content type="string" default="" />
|
|
|
05afe3 |
+ </parameter>
|
|
|
05afe3 |
+ <parameter name="alias_range_name" unique="1" required="0">
|
|
|
05afe3 |
+ <longdesc lang="en">Subnet name for the Alias IP</longdesc>
|
|
|
05afe3 |
+ <shortdesc lang="en">Subnet name for the Alias IP</shortdesc>
|
|
|
05afe3 |
+ <content type="string" default="" />
|
|
|
05afe3 |
+ </parameter>
|
|
|
05afe3 |
<parameter name="hostlist" unique="1" required="0">
|
|
|
05afe3 |
<longdesc lang="en">List of hosts in the cluster</longdesc>
|
|
|
05afe3 |
<shortdesc lang="en">Host list</shortdesc>
|
|
|
05afe3 |
@@ -65,16 +75,6 @@ METADATA = \
|
|
|
05afe3 |
<shortdesc lang="en">Stackdriver-logging support</shortdesc>
|
|
|
05afe3 |
<content type="boolean" default="" />
|
|
|
05afe3 |
</parameter>
|
|
|
05afe3 |
- <parameter name="alias_ip" unique="1" required="1">
|
|
|
05afe3 |
- <longdesc lang="en">IP Address to be added including CIDR. E.g 192.168.0.1/32</longdesc>
|
|
|
05afe3 |
- <shortdesc lang="en">IP Address to be added including CIDR. E.g 192.168.0.1/32</shortdesc>
|
|
|
05afe3 |
- <content type="string" default="" />
|
|
|
05afe3 |
- </parameter>
|
|
|
05afe3 |
- <parameter name="alias_range_name" unique="1" required="0">
|
|
|
05afe3 |
- <longdesc lang="en">Subnet name for the Alias IP2</longdesc>
|
|
|
05afe3 |
- <shortdesc lang="en">Subnet name for the Alias IP</shortdesc>
|
|
|
05afe3 |
- <content type="string" default="" />
|
|
|
05afe3 |
- </parameter>
|
|
|
05afe3 |
</parameters>
|
|
|
05afe3 |
<actions>
|
|
|
05afe3 |
<action name="start" timeout="300" />
|
|
|
05afe3 |
|
|
|
05afe3 |
From 716d69040dba7a769efb5a60eca934fdd65585f2 Mon Sep 17 00:00:00 2001
|
|
|
05afe3 |
From: Oyvind Albrigtsen <oalbrigt@redhat.com>
|
|
|
05afe3 |
Date: Mon, 23 Jul 2018 11:17:00 +0200
|
|
|
05afe3 |
Subject: [PATCH 4/5] gcp-vpc-move-route: use Python library
|
|
|
05afe3 |
|
|
|
05afe3 |
---
|
|
|
05afe3 |
heartbeat/gcp-vpc-move-route.in | 58 ++++++++++++++++++++---------------------
|
|
|
05afe3 |
1 file changed, 28 insertions(+), 30 deletions(-)
|
|
|
05afe3 |
|
|
|
05afe3 |
diff --git a/heartbeat/gcp-vpc-move-route.in b/heartbeat/gcp-vpc-move-route.in
|
|
|
05afe3 |
index 566a70f86..125289d86 100644
|
|
|
05afe3 |
--- a/heartbeat/gcp-vpc-move-route.in
|
|
|
05afe3 |
+++ b/heartbeat/gcp-vpc-move-route.in
|
|
|
05afe3 |
@@ -39,6 +39,11 @@ import os
|
|
|
05afe3 |
import sys
|
|
|
05afe3 |
import time
|
|
|
05afe3 |
|
|
|
05afe3 |
+OCF_FUNCTIONS_DIR="%s/lib/heartbeat" % os.environ.get("OCF_ROOT")
|
|
|
05afe3 |
+sys.path.append(OCF_FUNCTIONS_DIR)
|
|
|
05afe3 |
+
|
|
|
05afe3 |
+from ocf import *
|
|
|
05afe3 |
+
|
|
|
05afe3 |
try:
|
|
|
05afe3 |
import googleapiclient.discovery
|
|
|
05afe3 |
import pyroute2
|
|
|
05afe3 |
@@ -55,12 +60,6 @@ else:
|
|
|
05afe3 |
import urllib2 as urlrequest
|
|
|
05afe3 |
|
|
|
05afe3 |
|
|
|
05afe3 |
-OCF_SUCCESS = 0
|
|
|
05afe3 |
-OCF_ERR_GENERIC = 1
|
|
|
05afe3 |
-OCF_ERR_UNIMPLEMENTED = 3
|
|
|
05afe3 |
-OCF_ERR_PERM = 4
|
|
|
05afe3 |
-OCF_ERR_CONFIGURED = 6
|
|
|
05afe3 |
-OCF_NOT_RUNNING = 7
|
|
|
05afe3 |
GCP_API_URL_PREFIX = 'https://www.googleapis.com/compute/v1'
|
|
|
05afe3 |
METADATA_SERVER = 'http://metadata.google.internal/computeMetadata/v1/'
|
|
|
05afe3 |
METADATA_HEADERS = {'Metadata-Flavor': 'Google'}
|
|
|
05afe3 |
@@ -199,18 +198,18 @@ def get_metadata(metadata_key, params=None, timeout=None):
|
|
|
05afe3 |
|
|
|
05afe3 |
def validate(ctx):
|
|
|
05afe3 |
if os.geteuid() != 0:
|
|
|
05afe3 |
- logging.error('You must run this agent as root')
|
|
|
05afe3 |
+ logger.error('You must run this agent as root')
|
|
|
05afe3 |
sys.exit(OCF_ERR_PERM)
|
|
|
05afe3 |
|
|
|
05afe3 |
try:
|
|
|
05afe3 |
ctx.conn = googleapiclient.discovery.build('compute', 'v1')
|
|
|
05afe3 |
except Exception as e:
|
|
|
05afe3 |
- logging.error('Couldn\'t connect with google api: ' + str(e))
|
|
|
05afe3 |
+ logger.error('Couldn\'t connect with google api: ' + str(e))
|
|
|
05afe3 |
sys.exit(OCF_ERR_CONFIGURED)
|
|
|
05afe3 |
|
|
|
05afe3 |
ctx.ip = os.environ.get('OCF_RESKEY_ip')
|
|
|
05afe3 |
if not ctx.ip:
|
|
|
05afe3 |
- logging.error('Missing ip parameter')
|
|
|
05afe3 |
+ logger.error('Missing ip parameter')
|
|
|
05afe3 |
sys.exit(OCF_ERR_CONFIGURED)
|
|
|
05afe3 |
|
|
|
05afe3 |
try:
|
|
|
05afe3 |
@@ -218,7 +217,7 @@ def validate(ctx):
|
|
|
05afe3 |
ctx.zone = get_metadata('instance/zone').split('/')[-1]
|
|
|
05afe3 |
ctx.project = get_metadata('project/project-id')
|
|
|
05afe3 |
except Exception as e:
|
|
|
05afe3 |
- logging.error(
|
|
|
05afe3 |
+ logger.error(
|
|
|
05afe3 |
'Instance information not found. Is this a GCE instance ?: %s', str(e))
|
|
|
05afe3 |
sys.exit(OCF_ERR_CONFIGURED)
|
|
|
05afe3 |
|
|
|
05afe3 |
@@ -234,7 +233,7 @@ def validate(ctx):
|
|
|
05afe3 |
atexit.register(ctx.iproute.close)
|
|
|
05afe3 |
idxs = ctx.iproute.link_lookup(ifname=ctx.interface)
|
|
|
05afe3 |
if not idxs:
|
|
|
05afe3 |
- logging.error('Network interface not found')
|
|
|
05afe3 |
+ logger.error('Network interface not found')
|
|
|
05afe3 |
sys.exit(OCF_ERR_CONFIGURED)
|
|
|
05afe3 |
ctx.iface_idx = idxs[0]
|
|
|
05afe3 |
|
|
|
05afe3 |
@@ -246,7 +245,7 @@ def check_conflicting_routes(ctx):
|
|
|
05afe3 |
response = request.execute()
|
|
|
05afe3 |
route_list = response.get('items', None)
|
|
|
05afe3 |
if route_list:
|
|
|
05afe3 |
- logging.error(
|
|
|
05afe3 |
+ logger.error(
|
|
|
05afe3 |
'Conflicting unnmanaged routes for destination %s/32 in VPC %s found : %s',
|
|
|
05afe3 |
ctx.ip, ctx.vpc_network, str(route_list))
|
|
|
05afe3 |
sys.exit(OCF_ERR_CONFIGURED)
|
|
|
05afe3 |
@@ -258,7 +257,7 @@ def route_release(ctx):
|
|
|
05afe3 |
|
|
|
05afe3 |
|
|
|
05afe3 |
def ip_monitor(ctx):
|
|
|
05afe3 |
- logging.info('IP monitor: checking local network configuration')
|
|
|
05afe3 |
+ logger.info('IP monitor: checking local network configuration')
|
|
|
05afe3 |
|
|
|
05afe3 |
def address_filter(addr):
|
|
|
05afe3 |
for attr in addr['attrs']:
|
|
|
05afe3 |
@@ -271,12 +270,12 @@ def ip_monitor(ctx):
|
|
|
05afe3 |
route = ctx.iproute.get_addr(
|
|
|
05afe3 |
index=ctx.iface_idx, match=address_filter)
|
|
|
05afe3 |
if not route:
|
|
|
05afe3 |
- logging.warn(
|
|
|
05afe3 |
+ logger.warning(
|
|
|
05afe3 |
'The floating IP %s is not locally configured on this instance (%s)',
|
|
|
05afe3 |
ctx.ip, ctx.instance)
|
|
|
05afe3 |
return OCF_NOT_RUNNING
|
|
|
05afe3 |
|
|
|
05afe3 |
- logging.debug(
|
|
|
05afe3 |
+ logger.debug(
|
|
|
05afe3 |
'The floating IP %s is correctly configured on this instance (%s)',
|
|
|
05afe3 |
ctx.ip, ctx.instance)
|
|
|
05afe3 |
return OCF_SUCCESS
|
|
|
05afe3 |
@@ -287,7 +286,7 @@ def ip_release(ctx):
|
|
|
05afe3 |
|
|
|
05afe3 |
|
|
|
05afe3 |
def ip_and_route_start(ctx):
|
|
|
05afe3 |
- logging.info('Bringing up the floating IP %s', ctx.ip)
|
|
|
05afe3 |
+ logger.info('Bringing up the floating IP %s', ctx.ip)
|
|
|
05afe3 |
|
|
|
05afe3 |
# Add a new entry in the routing table
|
|
|
05afe3 |
# If the route entry exists and is pointing to another instance, take it over
|
|
|
05afe3 |
@@ -322,7 +321,7 @@ def ip_and_route_start(ctx):
|
|
|
05afe3 |
request.execute()
|
|
|
05afe3 |
except googleapiclient.errors.HttpError as e:
|
|
|
05afe3 |
if e.resp.status == 404:
|
|
|
05afe3 |
- logging.error('VPC network not found')
|
|
|
05afe3 |
+ logger.error('VPC network not found')
|
|
|
05afe3 |
sys.exit(OCF_ERR_CONFIGURED)
|
|
|
05afe3 |
else:
|
|
|
05afe3 |
raise
|
|
|
05afe3 |
@@ -336,11 +335,11 @@ def ip_and_route_start(ctx):
|
|
|
05afe3 |
|
|
|
05afe3 |
ctx.iproute.addr('add', index=ctx.iface_idx, address=ctx.ip, mask=32)
|
|
|
05afe3 |
ctx.iproute.link('set', index=ctx.iface_idx, state='up')
|
|
|
05afe3 |
- logging.info('Successfully brought up the floating IP %s', ctx.ip)
|
|
|
05afe3 |
+ logger.info('Successfully brought up the floating IP %s', ctx.ip)
|
|
|
05afe3 |
|
|
|
05afe3 |
|
|
|
05afe3 |
def route_monitor(ctx):
|
|
|
05afe3 |
- logging.info('GCP route monitor: checking route table')
|
|
|
05afe3 |
+ logger.info('GCP route monitor: checking route table')
|
|
|
05afe3 |
|
|
|
05afe3 |
# Ensure that there is no route that we are not aware of that is also handling our IP
|
|
|
05afe3 |
check_conflicting_routes
|
|
|
05afe3 |
@@ -360,39 +359,38 @@ def route_monitor(ctx):
|
|
|
05afe3 |
instance_url = '%s/projects/%s/zones/%s/instances/%s' % (
|
|
|
05afe3 |
GCP_API_URL_PREFIX, ctx.project, ctx.zone, ctx.instance)
|
|
|
05afe3 |
if routed_to_instance != instance_url:
|
|
|
05afe3 |
- logging.warn(
|
|
|
05afe3 |
+ logger.warning(
|
|
|
05afe3 |
'The floating IP %s is not routed to this instance (%s) but to instance %s',
|
|
|
05afe3 |
ctx.ip, ctx.instance, routed_to_instance.split('/')[-1])
|
|
|
05afe3 |
return OCF_NOT_RUNNING
|
|
|
05afe3 |
|
|
|
05afe3 |
- logging.debug(
|
|
|
05afe3 |
+ logger.debug(
|
|
|
05afe3 |
'The floating IP %s is correctly routed to this instance (%s)',
|
|
|
05afe3 |
ctx.ip, ctx.instance)
|
|
|
05afe3 |
return OCF_SUCCESS
|
|
|
05afe3 |
|
|
|
05afe3 |
|
|
|
05afe3 |
def ip_and_route_stop(ctx):
|
|
|
05afe3 |
- logging.info('Bringing down the floating IP %s', ctx.ip)
|
|
|
05afe3 |
+ logger.info('Bringing down the floating IP %s', ctx.ip)
|
|
|
05afe3 |
|
|
|
05afe3 |
# Delete the route entry
|
|
|
05afe3 |
# If the route entry exists and is pointing to another instance, don't touch it
|
|
|
05afe3 |
if route_monitor(ctx) == OCF_NOT_RUNNING:
|
|
|
05afe3 |
- logging.info(
|
|
|
05afe3 |
+ logger.info(
|
|
|
05afe3 |
'The floating IP %s is already not routed to this instance (%s)',
|
|
|
05afe3 |
ctx.ip, ctx.instance)
|
|
|
05afe3 |
else:
|
|
|
05afe3 |
route_release(ctx)
|
|
|
05afe3 |
|
|
|
05afe3 |
if ip_monitor(ctx) == OCF_NOT_RUNNING:
|
|
|
05afe3 |
- logging.info('The floating IP %s is already down', ctx.ip)
|
|
|
05afe3 |
+ logger.info('The floating IP %s is already down', ctx.ip)
|
|
|
05afe3 |
else:
|
|
|
05afe3 |
ip_release(ctx)
|
|
|
05afe3 |
|
|
|
05afe3 |
|
|
|
05afe3 |
def configure_logs(ctx):
|
|
|
05afe3 |
# Prepare logging
|
|
|
05afe3 |
- logging.basicConfig(
|
|
|
05afe3 |
- format='gcp:route - %(levelname)s - %(message)s', level=logging.INFO)
|
|
|
05afe3 |
+ global logger
|
|
|
05afe3 |
logging.getLogger('googleapiclient').setLevel(logging.WARN)
|
|
|
05afe3 |
logging_env = os.environ.get('OCF_RESKEY_stackdriver_logging')
|
|
|
05afe3 |
if logging_env:
|
|
|
05afe3 |
@@ -406,10 +404,10 @@ def configure_logs(ctx):
|
|
|
05afe3 |
handler.setLevel(logging.INFO)
|
|
|
05afe3 |
formatter = logging.Formatter('gcp:route "%(message)s"')
|
|
|
05afe3 |
handler.setFormatter(formatter)
|
|
|
05afe3 |
- root_logger = logging.getLogger()
|
|
|
05afe3 |
- root_logger.addHandler(handler)
|
|
|
05afe3 |
+ log.addHandler(handler)
|
|
|
05afe3 |
+ logger = logging.LoggerAdapter(log, {'OCF_RESOURCE_INSTANCE': OCF_RESOURCE_INSTANCE})
|
|
|
05afe3 |
except ImportError:
|
|
|
05afe3 |
- logging.error('Couldn\'t import google.cloud.logging, '
|
|
|
05afe3 |
+ logger.error('Couldn\'t import google.cloud.logging, '
|
|
|
05afe3 |
'disabling Stackdriver-logging support')
|
|
|
05afe3 |
|
|
|
05afe3 |
|
|
|
05afe3 |
@@ -434,7 +432,7 @@ def main():
|
|
|
05afe3 |
else:
|
|
|
05afe3 |
usage = 'usage: %s {start|stop|monitor|status|meta-data|validate-all}' % \
|
|
|
05afe3 |
os.path.basename(sys.argv[0])
|
|
|
05afe3 |
- logging.error(usage)
|
|
|
05afe3 |
+ logger.error(usage)
|
|
|
05afe3 |
sys.exit(OCF_ERR_UNIMPLEMENTED)
|
|
|
05afe3 |
|
|
|
05afe3 |
|
|
|
05afe3 |
|
|
|
05afe3 |
From 6ec7e87693a51cbb16a1822e6d15f1dbfc11f8e6 Mon Sep 17 00:00:00 2001
|
|
|
05afe3 |
From: Oyvind Albrigtsen <oalbrigt@redhat.com>
|
|
|
05afe3 |
Date: Mon, 23 Jul 2018 15:55:48 +0200
|
|
|
05afe3 |
Subject: [PATCH 5/5] Python: add logging.basicConfig() to support background
|
|
|
05afe3 |
logging
|
|
|
05afe3 |
|
|
|
05afe3 |
---
|
|
|
05afe3 |
heartbeat/ocf.py | 1 +
|
|
|
05afe3 |
1 file changed, 1 insertion(+)
|
|
|
05afe3 |
|
|
|
05afe3 |
diff --git a/heartbeat/ocf.py b/heartbeat/ocf.py
|
|
|
05afe3 |
index 12be7a2a4..36e7ccccd 100644
|
|
|
05afe3 |
--- a/heartbeat/ocf.py
|
|
|
05afe3 |
+++ b/heartbeat/ocf.py
|
|
|
05afe3 |
@@ -94,6 +94,7 @@ def emit(self, record):
|
|
|
05afe3 |
HA_LOGFILE = env.get("HA_LOGFILE")
|
|
|
05afe3 |
HA_DEBUGLOG = env.get("HA_DEBUGLOG")
|
|
|
05afe3 |
|
|
|
05afe3 |
+logging.basicConfig()
|
|
|
05afe3 |
log = logging.getLogger(os.path.basename(argv[0]))
|
|
|
05afe3 |
log.setLevel(logging.DEBUG)
|
|
|
05afe3 |
|