File: dibbler.py

package info (click to toggle)
neutron 2:13.0.2-15
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 30,764 kB
  • sloc: python: 188,554; sh: 1,060; makefile: 246
file content (193 lines) | stat: -rw-r--r-- 7,239 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
# Copyright 2015 Cisco Systems
# All Rights Reserved.
#
#    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 shutil

import jinja2
from neutron_lib import constants as lib_const
from neutron_lib.utils import file as file_utils
from oslo_config import cfg
from oslo_log import log as logging
import six

from neutron.agent.linux import external_process
from neutron.agent.linux import pd
from neutron.agent.linux import pd_driver
from neutron.agent.linux import utils

LOG = logging.getLogger(__name__)

PD_SERVICE_NAME = 'dibbler'
CONFIG_TEMPLATE = jinja2.Template("""
# Config for dibbler-client.

# Use enterprise number based duid
duid-type duid-en {{ enterprise_number }} {{ va_id }}

# 8 (Debug) is most verbose. 7 (Info) is usually the best option
log-level 8

# No automatic downlink address assignment
downlink-prefix-ifaces "none"

# Use script to notify l3_agent of assigned prefix
script {{ script_path }}

# Ask for prefix over the external gateway interface
iface {{ interface_name }} {
# Bind to generated LLA
bind-to-address {{ bind_address }}
# ask for address
   {% if hint_prefix != None %}
    pd 1 {
        prefix {{ hint_prefix }}
    }
   {% else %}
    pd 1
   {% endif %}
}
""")

# The first line must be #!/usr/bin/env bash
SCRIPT_TEMPLATE = jinja2.Template("""#!/usr/bin/env bash

exec neutron-pd-notify $1 {{ prefix_path }} {{ l3_agent_pid }}
""")


class PDDibbler(pd_driver.PDDriverBase):
    def __init__(self, router_id, subnet_id, ri_ifname):
        super(PDDibbler, self).__init__(router_id, subnet_id, ri_ifname)
        self.requestor_id = "%s:%s:%s" % (self.router_id,
                                          self.subnet_id,
                                          self.ri_ifname)
        self.dibbler_client_working_area = "%s/%s" % (cfg.CONF.pd_confs,
                                                      self.requestor_id)
        self.prefix_path = "%s/prefix" % self.dibbler_client_working_area
        self.pid_path = "%s/client.pid" % self.dibbler_client_working_area
        self.converted_subnet_id = self.subnet_id.replace('-', '')

    def _is_dibbler_client_running(self):
        return utils.get_value_from_file(self.pid_path)

    def _generate_dibbler_conf(self, ex_gw_ifname, lla, hint_prefix):
        dcwa = self.dibbler_client_working_area
        script_path = utils.get_conf_file_name(dcwa, 'notify', 'sh', True)
        buf = six.StringIO()
        buf.write('%s' % SCRIPT_TEMPLATE.render(
                             prefix_path=self.prefix_path,
                             l3_agent_pid=os.getpid()))
        file_utils.replace_file(script_path, buf.getvalue())
        os.chmod(script_path, 0o744)

        dibbler_conf = utils.get_conf_file_name(dcwa, 'client', 'conf', False)
        buf = six.StringIO()
        buf.write('%s' % CONFIG_TEMPLATE.render(
                             enterprise_number=cfg.CONF.vendor_pen,
                             va_id='0x%s' % self.converted_subnet_id,
                             script_path='"%s/notify.sh"' % dcwa,
                             interface_name='"%s"' % ex_gw_ifname,
                             bind_address='%s' % lla,
                             hint_prefix=hint_prefix))

        file_utils.replace_file(dibbler_conf, buf.getvalue())
        return dcwa

    def _spawn_dibbler(self, pmon, router_ns, dibbler_conf):
        def callback(pid_file):
            dibbler_cmd = ['dibbler-client',
                           'start',
                           '-w', '%s' % dibbler_conf]
            return dibbler_cmd

        pm = external_process.ProcessManager(
            uuid=self.requestor_id,
            default_cmd_callback=callback,
            namespace=router_ns,
            service=PD_SERVICE_NAME,
            conf=cfg.CONF,
            pid_file=self.pid_path)
        pm.enable(reload_cfg=False)
        pmon.register(uuid=self.requestor_id,
                      service_name=PD_SERVICE_NAME,
                      monitored_process=pm)

    def enable(self, pmon, router_ns, ex_gw_ifname, lla, prefix=None):
        LOG.debug("Enable IPv6 PD for router %s subnet %s ri_ifname %s",
                  self.router_id, self.subnet_id, self.ri_ifname)
        if not self._is_dibbler_client_running():
            dibbler_conf = self._generate_dibbler_conf(ex_gw_ifname,
                                                       lla, prefix)
            self._spawn_dibbler(pmon, router_ns, dibbler_conf)
            LOG.debug("dibbler client enabled for router %s subnet %s"
                      " ri_ifname %s",
                      self.router_id, self.subnet_id, self.ri_ifname)

    def disable(self, pmon, router_ns, switch_over=False):
        LOG.debug("Disable IPv6 PD for router %s subnet %s ri_ifname %s",
                  self.router_id, self.subnet_id, self.ri_ifname)
        dcwa = self.dibbler_client_working_area

        def callback(pid_file):
            dibbler_cmd = ['dibbler-client',
                           'stop',
                           '-w', '%s' % dcwa]
            return dibbler_cmd

        pmon.unregister(uuid=self.requestor_id,
                        service_name=PD_SERVICE_NAME)
        pm = external_process.ProcessManager(
                uuid=self.requestor_id,
                namespace=router_ns,
                service=PD_SERVICE_NAME,
                conf=cfg.CONF,
                pid_file=self.pid_path)
        if switch_over:
            pm.disable()
        else:
            pm.disable(get_stop_command=callback)
        shutil.rmtree(dcwa, ignore_errors=True)
        LOG.debug("dibbler client disabled for router %s subnet %s "
                  "ri_ifname %s",
                  self.router_id, self.subnet_id, self.ri_ifname)

    def get_prefix(self):
        prefix = utils.get_value_from_file(self.prefix_path)
        if not prefix:
            prefix = lib_const.PROVISIONAL_IPV6_PD_PREFIX
        return prefix

    @staticmethod
    def get_sync_data():
        try:
            requestor_ids = os.listdir(cfg.CONF.pd_confs)
        except OSError:
            return []

        sync_data = []
        requestors = (r.split(':') for r in requestor_ids if r.count(':') == 2)
        for router_id, subnet_id, ri_ifname in requestors:
            pd_info = pd.PDInfo()
            pd_info.router_id = router_id
            pd_info.subnet_id = subnet_id
            pd_info.ri_ifname = ri_ifname
            pd_info.driver = PDDibbler(router_id, subnet_id, ri_ifname)
            pd_info.client_started = (
                pd_info.driver._is_dibbler_client_running())
            pd_info.prefix = pd_info.driver.get_prefix()
            sync_data.append(pd_info)

        return sync_data