"Fossies" - the Fresh Open Source Software Archive

Member "octavia-8.0.0/octavia/amphorae/backends/agent/api_server/keepalivedlvs.py" (14 Apr 2021, 14777 Bytes) of package /linux/misc/openstack/octavia-8.0.0.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. For more information about "keepalivedlvs.py" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 7.1.1_vs_8.0.0.

    1 #    Copyright 2011-2014 OpenStack Foundation
    2 #
    3 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
    4 #    not use this file except in compliance with the License. You may obtain
    5 #    a copy of the License at
    6 #
    7 #         http://www.apache.org/licenses/LICENSE-2.0
    8 #
    9 #    Unless required by applicable law or agreed to in writing, software
   10 #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
   11 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
   12 #    License for the specific language governing permissions and limitations
   13 #    under the License.
   14 
   15 import os
   16 import re
   17 import shutil
   18 import stat
   19 import subprocess
   20 
   21 import flask
   22 import jinja2
   23 from oslo_config import cfg
   24 from oslo_log import log as logging
   25 import webob
   26 from werkzeug import exceptions
   27 
   28 from octavia.amphorae.backends.agent.api_server import loadbalancer
   29 from octavia.amphorae.backends.agent.api_server import lvs_listener_base
   30 from octavia.amphorae.backends.agent.api_server import util
   31 from octavia.common import constants as consts
   32 
   33 BUFFER = 100
   34 CHECK_SCRIPT_NAME = 'udp_check.sh'
   35 CONF = cfg.CONF
   36 KEEPALIVED_CHECK_SCRIPT_NAME = 'lvs_udp_check.sh'
   37 LOG = logging.getLogger(__name__)
   38 
   39 j2_env = jinja2.Environment(autoescape=True, loader=jinja2.FileSystemLoader(
   40     os.path.dirname(os.path.realpath(__file__)) + consts.AGENT_API_TEMPLATES))
   41 UPSTART_TEMPLATE = j2_env.get_template(consts.KEEPALIVED_JINJA2_UPSTART)
   42 SYSVINIT_TEMPLATE = j2_env.get_template(consts.KEEPALIVED_JINJA2_SYSVINIT)
   43 SYSTEMD_TEMPLATE = j2_env.get_template(consts.KEEPALIVED_JINJA2_SYSTEMD)
   44 check_script_file_template = j2_env.get_template(
   45     consts.KEEPALIVED_CHECK_SCRIPT)
   46 
   47 
   48 class KeepalivedLvs(lvs_listener_base.LvsListenerApiServerBase):
   49 
   50     _SUBSCRIBED_AMP_COMPILE = ['keepalived', 'ipvsadm']
   51 
   52     def upload_lvs_listener_config(self, listener_id):
   53         stream = loadbalancer.Wrapped(flask.request.stream)
   54         NEED_CHECK = True
   55 
   56         if not os.path.exists(util.keepalived_lvs_dir()):
   57             os.makedirs(util.keepalived_lvs_dir())
   58         if not os.path.exists(util.keepalived_backend_check_script_dir()):
   59             current_file_dir, _ = os.path.split(os.path.abspath(__file__))
   60 
   61             try:
   62                 script_dir = os.path.join(os.path.abspath(
   63                     os.path.join(current_file_dir, '../..')), 'utils')
   64                 assert True is os.path.exists(script_dir)
   65                 assert True is os.path.exists(os.path.join(
   66                     script_dir, CHECK_SCRIPT_NAME))
   67             except Exception as e:
   68                 raise exceptions.Conflict(
   69                     description='%(file_name)s not Found for '
   70                                 'UDP Listener %(listener_id)s' %
   71                                 {'file_name': CHECK_SCRIPT_NAME,
   72                                  'listener_id': listener_id}) from e
   73             os.makedirs(util.keepalived_backend_check_script_dir())
   74             shutil.copy2(os.path.join(script_dir, CHECK_SCRIPT_NAME),
   75                          util.keepalived_backend_check_script_path())
   76             os.chmod(util.keepalived_backend_check_script_path(), stat.S_IEXEC)
   77         # Based on current topology setting, only the amphora instances in
   78         # Active-Standby topology will create the directory below. So for
   79         # Single topology, it should not create the directory and the check
   80         # scripts for status change.
   81         if (CONF.controller_worker.loadbalancer_topology !=
   82                 consts.TOPOLOGY_ACTIVE_STANDBY):
   83             NEED_CHECK = False
   84 
   85         conf_file = util.keepalived_lvs_cfg_path(listener_id)
   86         flags = os.O_WRONLY | os.O_CREAT | os.O_TRUNC
   87         # mode 00644
   88         mode = stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH
   89         with os.fdopen(os.open(conf_file, flags, mode), 'wb') as f:
   90             b = stream.read(BUFFER)
   91             while b:
   92                 f.write(b)
   93                 b = stream.read(BUFFER)
   94 
   95         init_system = util.get_os_init_system()
   96 
   97         file_path = util.keepalived_lvs_init_path(init_system, listener_id)
   98 
   99         if init_system == consts.INIT_SYSTEMD:
  100             template = SYSTEMD_TEMPLATE
  101 
  102             # Render and install the network namespace systemd service
  103             util.install_netns_systemd_service()
  104             util.run_systemctl_command(
  105                 consts.ENABLE, consts.AMP_NETNS_SVC_PREFIX)
  106         elif init_system == consts.INIT_UPSTART:
  107             template = UPSTART_TEMPLATE
  108         elif init_system == consts.INIT_SYSVINIT:
  109             template = SYSVINIT_TEMPLATE
  110         else:
  111             raise util.UnknownInitError()
  112 
  113         # Render and install the keepalivedlvs init script
  114         if init_system == consts.INIT_SYSTEMD:
  115             # mode 00644
  116             mode = stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH
  117         else:
  118             # mode 00755
  119             mode = (stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP |
  120                     stat.S_IROTH | stat.S_IXOTH)
  121         keepalived_pid, vrrp_pid, check_pid = util.keepalived_lvs_pids_path(
  122             listener_id)
  123         if not os.path.exists(file_path):
  124             with os.fdopen(os.open(file_path, flags, mode), 'w') as text_file:
  125                 text = template.render(
  126                     keepalived_pid=keepalived_pid,
  127                     vrrp_pid=vrrp_pid,
  128                     check_pid=check_pid,
  129                     keepalived_cmd=consts.KEEPALIVED_CMD,
  130                     keepalived_cfg=util.keepalived_lvs_cfg_path(listener_id),
  131                     amphora_nsname=consts.AMPHORA_NAMESPACE,
  132                     amphora_netns=consts.AMP_NETNS_SVC_PREFIX,
  133                     administrative_log_facility=(
  134                         CONF.amphora_agent.administrative_log_facility),
  135                 )
  136                 text_file.write(text)
  137 
  138         # Make sure the keepalivedlvs service is enabled on boot
  139         if init_system == consts.INIT_SYSTEMD:
  140             util.run_systemctl_command(
  141                 consts.ENABLE, "octavia-keepalivedlvs-%s" % str(listener_id))
  142         elif init_system == consts.INIT_SYSVINIT:
  143             init_enable_cmd = "insserv {file}".format(file=file_path)
  144             try:
  145                 subprocess.check_output(init_enable_cmd.split(),
  146                                         stderr=subprocess.STDOUT)
  147             except subprocess.CalledProcessError as e:
  148                 LOG.debug('Failed to enable '
  149                           'octavia-keepalivedlvs service: '
  150                           '%(err)s', {'err': str(e)})
  151                 return webob.Response(json=dict(
  152                     message="Error enabling "
  153                             "octavia-keepalivedlvs service",
  154                     details=e.output), status=500)
  155 
  156         if NEED_CHECK:
  157             # inject the check script for keepalived process
  158             script_path = os.path.join(util.keepalived_check_scripts_dir(),
  159                                        KEEPALIVED_CHECK_SCRIPT_NAME)
  160             if not os.path.exists(script_path):
  161                 if not os.path.exists(util.keepalived_check_scripts_dir()):
  162                     os.makedirs(util.keepalived_check_scripts_dir())
  163 
  164                 with os.fdopen(os.open(script_path, flags, stat.S_IEXEC),
  165                                'w') as script_file:
  166                     text = check_script_file_template.render(
  167                         consts=consts,
  168                         init_system=init_system,
  169                         keepalived_lvs_pid_dir=util.keepalived_lvs_dir()
  170                     )
  171                     script_file.write(text)
  172             util.vrrp_check_script_update(None, consts.AMP_ACTION_START)
  173 
  174         res = webob.Response(json={'message': 'OK'}, status=200)
  175         res.headers['ETag'] = stream.get_md5()
  176         return res
  177 
  178     def _check_lvs_listener_exists(self, listener_id):
  179         if not os.path.exists(util.keepalived_lvs_cfg_path(listener_id)):
  180             raise exceptions.HTTPException(
  181                 response=webob.Response(json=dict(
  182                     message='UDP Listener Not Found',
  183                     details="No UDP listener with UUID: {0}".format(
  184                         listener_id)), status=404))
  185 
  186     def get_lvs_listener_config(self, listener_id):
  187         """Gets the keepalivedlvs config
  188 
  189         :param listener_id: the id of the listener
  190         """
  191         self._check_lvs_listener_exists(listener_id)
  192         with open(util.keepalived_lvs_cfg_path(listener_id), 'r') as file:
  193             cfg = file.read()
  194             resp = webob.Response(cfg, content_type='text/plain')
  195             return resp
  196 
  197     def manage_lvs_listener(self, listener_id, action):
  198         action = action.lower()
  199         if action not in [consts.AMP_ACTION_START,
  200                           consts.AMP_ACTION_STOP,
  201                           consts.AMP_ACTION_RELOAD]:
  202             return webob.Response(json=dict(
  203                 message='Invalid Request',
  204                 details="Unknown action: {0}".format(action)), status=400)
  205 
  206         # When octavia requests a reload of keepalived, force a restart since
  207         # a keepalived reload doesn't restore members in their initial state.
  208         #
  209         # TODO(gthiemonge) remove this when keepalived>=2.0.14 is widely use
  210         if action == consts.AMP_ACTION_RELOAD:
  211             action = consts.AMP_ACTION_RESTART
  212 
  213         self._check_lvs_listener_exists(listener_id)
  214         if action == consts.AMP_ACTION_RELOAD:
  215             if consts.OFFLINE == self._check_lvs_listener_status(listener_id):
  216                 action = consts.AMP_ACTION_START
  217 
  218         cmd = ("/usr/sbin/service "
  219                "octavia-keepalivedlvs-{listener_id} "
  220                "{action}".format(listener_id=listener_id, action=action))
  221 
  222         try:
  223             subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT)
  224         except subprocess.CalledProcessError as e:
  225             LOG.debug('Failed to %s keepalivedlvs listener %s',
  226                       listener_id + ' : ' + action, str(e))
  227             return webob.Response(json=dict(
  228                 message=("Failed to {0} keepalivedlvs listener {1}"
  229                          .format(action, listener_id)),
  230                 details=e.output), status=500)
  231 
  232         return webob.Response(
  233             json=dict(message='OK',
  234                       details='keepalivedlvs listener {listener_id} '
  235                               '{action}ed'.format(listener_id=listener_id,
  236                                                   action=action)),
  237             status=202)
  238 
  239     def _check_lvs_listener_status(self, listener_id):
  240         if os.path.exists(util.keepalived_lvs_pids_path(listener_id)[0]):
  241             if os.path.exists(os.path.join(
  242                     '/proc', util.get_keepalivedlvs_pid(listener_id))):
  243                 # Check if the listener is disabled
  244                 with open(util.keepalived_lvs_cfg_path(listener_id),
  245                           'r') as file:
  246                     cfg = file.read()
  247                     m = re.search('virtual_server', cfg)
  248                     if m:
  249                         return consts.ACTIVE
  250                     return consts.OFFLINE
  251             return consts.ERROR
  252         return consts.OFFLINE
  253 
  254     def get_all_lvs_listeners_status(self):
  255         """Gets the status of all UDP listeners
  256 
  257         Gets the status of all UDP listeners on the amphora.
  258         """
  259         listeners = list()
  260 
  261         for lvs_listener in util.get_lvs_listeners():
  262             status = self._check_lvs_listener_status(lvs_listener)
  263             listeners.append({
  264                 'status': status,
  265                 'uuid': lvs_listener,
  266                 'type': 'UDP',
  267             })
  268         return listeners
  269 
  270     def delete_lvs_listener(self, listener_id):
  271         try:
  272             self._check_lvs_listener_exists(listener_id)
  273         except exceptions.HTTPException:
  274             return webob.Response(json={'message': 'OK'})
  275 
  276         # check if that keepalived is still running and if stop it
  277         keepalived_pid, vrrp_pid, check_pid = util.keepalived_lvs_pids_path(
  278             listener_id)
  279         if os.path.exists(keepalived_pid) and os.path.exists(
  280                 os.path.join('/proc',
  281                              util.get_keepalivedlvs_pid(listener_id))):
  282             cmd = ("/usr/sbin/service "
  283                    "octavia-keepalivedlvs-{0} stop".format(listener_id))
  284             try:
  285                 subprocess.check_output(cmd.split(), stderr=subprocess.STDOUT)
  286             except subprocess.CalledProcessError as e:
  287                 LOG.error("Failed to stop keepalivedlvs service: %s", str(e))
  288                 return webob.Response(json=dict(
  289                     message="Error stopping keepalivedlvs",
  290                     details=e.output), status=500)
  291 
  292         # Since the lvs check script based on the keepalived pid file for
  293         # checking whether it is alived. So here, we had stop the keepalived
  294         # process by the previous step, must make sure the pid files are not
  295         # exist.
  296         if (os.path.exists(keepalived_pid) or
  297                 os.path.exists(vrrp_pid) or os.path.exists(check_pid)):
  298             for pid in [keepalived_pid, vrrp_pid, check_pid]:
  299                 os.remove(pid)
  300 
  301         # disable the service
  302         init_system = util.get_os_init_system()
  303         init_path = util.keepalived_lvs_init_path(init_system, listener_id)
  304 
  305         if init_system == consts.INIT_SYSTEMD:
  306             util.run_systemctl_command(
  307                 consts.DISABLE, "octavia-keepalivedlvs-%s" % str(listener_id))
  308         elif init_system == consts.INIT_SYSVINIT:
  309             init_disable_cmd = "insserv -r {file}".format(file=init_path)
  310         elif init_system != consts.INIT_UPSTART:
  311             raise util.UnknownInitError()
  312 
  313         if init_system == consts.INIT_SYSVINIT:
  314             try:
  315                 subprocess.check_output(init_disable_cmd.split(),
  316                                         stderr=subprocess.STDOUT)
  317             except subprocess.CalledProcessError as e:
  318                 LOG.error("Failed to disable "
  319                           "octavia-keepalivedlvs-%(list)s service: "
  320                           "%(err)s", {'list': listener_id, 'err': str(e)})
  321                 return webob.Response(json=dict(
  322                     message=(
  323                         "Error disabling octavia-keepalivedlvs-"
  324                         "{0} service".format(listener_id)),
  325                     details=e.output), status=500)
  326 
  327         # delete init script ,config file and log file for that listener
  328         if os.path.exists(init_path):
  329             os.remove(init_path)
  330         if os.path.exists(util.keepalived_lvs_cfg_path(listener_id)):
  331             os.remove(util.keepalived_lvs_cfg_path(listener_id))
  332 
  333         return webob.Response(json={'message': 'OK'})