"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.

    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'})