Set lock_path correctly.
[openstack-build/neutron-build.git] / neutron / agent / l3 / namespace_manager.py
1 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
2 #    not use this file except in compliance with the License. You may obtain
3 #    a copy of the License at
4 #
5 #         http://www.apache.org/licenses/LICENSE-2.0
6 #
7 #    Unless required by applicable law or agreed to in writing, software
8 #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
9 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
10 #    License for the specific language governing permissions and limitations
11 #    under the License.
12
13 from oslo_log import log as logging
14
15 from neutron._i18n import _LE
16 from neutron.agent.l3 import dvr_fip_ns
17 from neutron.agent.l3 import dvr_snat_ns
18 from neutron.agent.l3 import namespaces
19 from neutron.agent.linux import external_process
20 from neutron.agent.linux import ip_lib
21
22 LOG = logging.getLogger(__name__)
23
24
25 class NamespaceManager(object):
26
27     """Keeps track of namespaces that need to be cleaned up.
28
29     This is a context manager that looks to clean up stale namespaces that
30     have not been touched by the end of the "with" statement it is called
31     in.  This formalizes the pattern used in the L3 agent which enumerated
32     all of the namespaces known to the system before a full sync.  Then,
33     after the full sync completed, it cleaned up any that were not touched
34     during the sync. The agent and this context manager use method keep_router
35     to communicate. In the "with" statement, the agent calls keep_router to
36     record the id's of the routers whose namespaces should be preserved.
37     Any other router and snat namespace present in the system will be deleted
38     by the __exit__ method of this context manager
39
40     This pattern can be more generally applicable to other resources
41     besides namespaces in the future because it is idempotent and, as such,
42     does not rely on state recorded at runtime in the agent so it handles
43     agent restarts gracefully.
44     """
45
46     ns_prefix_to_class_map = {
47         namespaces.NS_PREFIX: namespaces.RouterNamespace,
48         dvr_snat_ns.SNAT_NS_PREFIX: dvr_snat_ns.SnatNamespace,
49         dvr_fip_ns.FIP_NS_PREFIX: dvr_fip_ns.FipNamespace,
50     }
51
52     def __init__(self, agent_conf, driver, metadata_driver=None):
53         """Initialize the NamespaceManager.
54
55         :param agent_conf: configuration from l3 agent
56         :param driver: to perform operations on devices
57         :param metadata_driver: used to cleanup stale metadata proxy processes
58         """
59         self.agent_conf = agent_conf
60         self.driver = driver
61         self._clean_stale = True
62         self.metadata_driver = metadata_driver
63         if metadata_driver:
64             self.process_monitor = external_process.ProcessMonitor(
65                 config=agent_conf,
66                 resource_type='router')
67
68     def __enter__(self):
69         self._all_namespaces = set()
70         self._ids_to_keep = set()
71         if self._clean_stale:
72             self._all_namespaces = self.list_all()
73         return self
74
75     def __exit__(self, exc_type, value, traceback):
76         # TODO(carl) Preserves old behavior of L3 agent where cleaning
77         # namespaces was only done once after restart.  Still a good idea?
78         if exc_type:
79             # An exception occurred in the caller's with statement
80             return False
81         if not self._clean_stale:
82             # No need to cleanup
83             return True
84         self._clean_stale = False
85
86         for ns in self._all_namespaces:
87             _ns_prefix, ns_id = self.get_prefix_and_id(ns)
88             if ns_id in self._ids_to_keep:
89                 continue
90             self._cleanup(_ns_prefix, ns_id)
91
92         return True
93
94     def keep_router(self, router_id):
95         self._ids_to_keep.add(router_id)
96
97     def keep_ext_net(self, ext_net_id):
98         self._ids_to_keep.add(ext_net_id)
99
100     def get_prefix_and_id(self, ns_name):
101         """Get the prefix and id from the namespace name.
102
103         :param ns_name: The name of the namespace
104         :returns: tuple with prefix and id or None if no prefix matches
105         """
106         prefix = namespaces.get_prefix_from_ns_name(ns_name)
107         if prefix in self.ns_prefix_to_class_map:
108             identifier = namespaces.get_id_from_ns_name(ns_name)
109             return (prefix, identifier)
110
111     def is_managed(self, ns_name):
112         """Return True if the namespace name passed belongs to this manager."""
113         return self.get_prefix_and_id(ns_name) is not None
114
115     def list_all(self):
116         """Get a set of all namespaces on host managed by this manager."""
117         try:
118             root_ip = ip_lib.IPWrapper()
119             namespaces = root_ip.get_namespaces()
120             return set(ns for ns in namespaces if self.is_managed(ns))
121         except RuntimeError:
122             LOG.exception(_LE('RuntimeError in obtaining namespace list for '
123                               'namespace cleanup.'))
124             return set()
125
126     def ensure_router_cleanup(self, router_id):
127         """Performs cleanup for a router"""
128         for ns in self.list_all():
129             if ns.endswith(router_id):
130                 ns_prefix, ns_id = self.get_prefix_and_id(ns)
131                 self._cleanup(ns_prefix, ns_id)
132
133     def _cleanup(self, ns_prefix, ns_id):
134         ns_class = self.ns_prefix_to_class_map[ns_prefix]
135         ns = ns_class(ns_id, self.agent_conf, self.driver, use_ipv6=False)
136         try:
137             if self.metadata_driver:
138                 # cleanup stale metadata proxy processes first
139                 self.metadata_driver.destroy_monitored_metadata_proxy(
140                     self.process_monitor, ns_id, self.agent_conf)
141             ns.delete()
142         except RuntimeError:
143             LOG.exception(_LE('Failed to destroy stale namespace %s'), ns)