41 plugin=None): |
43 plugin=None): |
42 super(Dnsmasq, self).__init__(conf, network, process_monitor, |
44 super(Dnsmasq, self).__init__(conf, network, process_monitor, |
43 version, plugin) |
45 version, plugin) |
44 self.device_manager = DeviceManager(self.conf, plugin) |
46 self.device_manager = DeviceManager(self.conf, plugin) |
45 |
47 |
|
48 # overrides method in DhcpLocalProcess due to no namespace support |
|
49 def _destroy_namespace_and_port(self): |
|
50 try: |
|
51 self.device_manager.destroy(self.network, self.interface_name) |
|
52 except RuntimeError: |
|
53 LOG.warning(_LW('Failed trying to delete interface: %s'), |
|
54 self.interface_name) |
|
55 |
46 def _build_cmdline_callback(self, pid_file): |
56 def _build_cmdline_callback(self, pid_file): |
|
57 # We ignore local resolv.conf if dns servers are specified |
|
58 # or if local resolution is explicitly disabled. |
|
59 _no_resolv = ( |
|
60 '--no-resolv' if self.conf.dnsmasq_dns_servers or |
|
61 not self.conf.dnsmasq_local_resolv else '') |
47 cmd = [ |
62 cmd = [ |
48 '/usr/lib/inet/dnsmasq', |
63 '/usr/lib/inet/dnsmasq', |
49 '--no-hosts', |
64 '--no-hosts', |
50 '--no-resolv', |
65 _no_resolv, |
51 '--strict-order', |
66 '--strict-order', |
52 '--bind-interfaces', |
67 '--bind-interfaces', |
53 '--interface=%s' % self.interface_name, |
68 '--interface=%s' % self.interface_name, |
54 '--except-interface=lo0', |
69 '--except-interface=lo0', |
55 '--pid-file=%s' % pid_file, |
70 '--pid-file=%s' % pid_file, |
56 '--dhcp-hostsfile=%s' % self.get_conf_file_name('host'), |
71 '--dhcp-hostsfile=%s' % self.get_conf_file_name('host'), |
57 '--addn-hosts=%s' % self.get_conf_file_name('addn_hosts'), |
72 '--addn-hosts=%s' % self.get_conf_file_name('addn_hosts'), |
58 '--dhcp-optsfile=%s' % self.get_conf_file_name('opts'), |
73 '--dhcp-optsfile=%s' % self.get_conf_file_name('opts'), |
59 '--dhcp-leasefile=%s' % self.get_conf_file_name('leases') |
74 '--dhcp-leasefile=%s' % self.get_conf_file_name('leases'), |
|
75 '--dhcp-match=set:ipxe,175', |
60 ] |
76 ] |
61 |
77 |
62 possible_leases = 0 |
78 possible_leases = 0 |
63 for i, subnet in enumerate(self.network.subnets): |
79 for i, subnet in enumerate(self.network.subnets): |
64 mode = None |
80 mode = None |
130 cmd.append('--domain=%s' % self.conf.dhcp_domain) |
146 cmd.append('--domain=%s' % self.conf.dhcp_domain) |
131 |
147 |
132 if self.conf.dhcp_broadcast_reply: |
148 if self.conf.dhcp_broadcast_reply: |
133 cmd.append('--dhcp-broadcast') |
149 cmd.append('--dhcp-broadcast') |
134 |
150 |
|
151 if self.conf.dnsmasq_base_log_dir: |
|
152 log_dir = os.path.join( |
|
153 self.conf.dnsmasq_base_log_dir, |
|
154 self.network.id) |
|
155 try: |
|
156 if not os.path.exists(log_dir): |
|
157 os.makedirs(log_dir) |
|
158 except OSError: |
|
159 LOG.error(_LE('Error while create dnsmasq log dir: %s'), |
|
160 log_dir) |
|
161 else: |
|
162 log_filename = os.path.join(log_dir, 'dhcp_dns_log') |
|
163 cmd.append('--log-queries') |
|
164 cmd.append('--log-dhcp') |
|
165 cmd.append('--log-facility=%s' % log_filename) |
|
166 |
135 return cmd |
167 return cmd |
136 |
168 |
137 def _release_lease(self, mac_address, ip): |
169 def _release_lease(self, mac_address, ip, client_id): |
138 """Release a DHCP lease.""" |
170 """Release a DHCP lease.""" |
|
171 if netaddr.IPAddress(ip).version == constants.IP_VERSION_6: |
|
172 # Note(SridharG) dhcp_release is only supported for IPv4 |
|
173 # addresses. For more details, please refer to man page. |
|
174 return |
|
175 |
139 cmd = ['/usr/lib/inet/dhcp_release', self.interface_name, |
176 cmd = ['/usr/lib/inet/dhcp_release', self.interface_name, |
140 ip, mac_address] |
177 ip, mac_address] |
|
178 if client_id: |
|
179 cmd.append(client_id) |
141 utils.execute(cmd) |
180 utils.execute(cmd) |
142 |
181 |
143 def _make_subnet_interface_ip_map(self): |
182 def _make_subnet_interface_ip_map(self): |
144 # TODO(gmoodalb): need to complete this when we support metadata |
183 # TODO(gmoodalb): need to complete this when we support metadata |
145 # in neutron-dhcp-agent as-well for isolated subnets |
184 # in neutron-dhcp-agent as-well for isolated subnets |
155 class DeviceManager(dhcp.DeviceManager): |
194 class DeviceManager(dhcp.DeviceManager): |
156 |
195 |
157 def __init__(self, conf, plugin): |
196 def __init__(self, conf, plugin): |
158 super(DeviceManager, self).__init__(conf, plugin) |
197 super(DeviceManager, self).__init__(conf, plugin) |
159 |
198 |
160 def setup_dhcp_port(self, network): |
199 def _set_default_route(self, network, device_name): |
161 """Create/update DHCP port for the host if needed and return port.""" |
200 """Sets the default gateway for this dhcp namespace. |
162 |
201 |
163 device_id = self.get_device_id(network) |
202 This method is idempotent and will only adjust the route if adjusting |
164 subnets = {} |
203 it would change it from what it already is. This makes it safe to call |
165 dhcp_enabled_subnet_ids = [] |
204 and avoids unnecessary perturbation of the system. |
166 for subnet in network.subnets: |
205 """ |
167 if subnet.enable_dhcp: |
206 pass |
168 dhcp_enabled_subnet_ids.append(subnet.id) |
207 |
169 subnets[subnet.id] = subnet |
208 def _setup_existing_dhcp_port(self, network, device_id, dhcp_subnets): |
170 |
209 """Set up the existing DHCP port, if there is one.""" |
171 dhcp_port = None |
210 |
|
211 # To avoid pylint thinking that port might be undefined after |
|
212 # the following loop... |
|
213 port = None |
|
214 |
|
215 # Look for an existing DHCP port for this network. |
172 for port in network.ports: |
216 for port in network.ports: |
173 port_device_id = getattr(port, 'device_id', None) |
217 port_device_id = getattr(port, 'device_id', None) |
174 port_device_owner = getattr(port, 'device_owner', None) |
218 port_device_owner = getattr(port, 'device_owner', None) |
175 |
|
176 # if the agent is started on a different node, then the |
|
177 # device_ids will be different since they are based off |
|
178 # hostname. |
|
179 if (port_device_id == device_id or |
219 if (port_device_id == device_id or |
180 (port_device_owner == constants.DEVICE_OWNER_DHCP and |
220 (port_device_owner == constants.DEVICE_OWNER_DHCP and |
181 port_device_id.startswith('dhcp'))): |
221 port_device_id.startswith('dhcp'))): |
182 port_fixed_ips = [] |
222 # If using gateway IPs on this port, we can skip the |
183 for fixed_ip in port.fixed_ips: |
223 # following code, whose purpose is just to review and |
184 port_fixed_ips.append({'subnet_id': fixed_ip.subnet_id, |
224 # update the Neutron-allocated IP addresses for the |
185 'ip_address': fixed_ip.ip_address}) |
225 # port. |
186 if fixed_ip.subnet_id in dhcp_enabled_subnet_ids: |
226 if self.driver.use_gateway_ips: |
187 dhcp_enabled_subnet_ids.remove(fixed_ip.subnet_id) |
227 return port |
188 |
228 # Otherwise break out, as we now have the DHCP port |
189 # If there are dhcp_enabled_subnet_ids here that means that |
229 # whose subnets and addresses we need to review. |
190 # we need to add those to the port and call update. |
|
191 if dhcp_enabled_subnet_ids: |
|
192 port_fixed_ips.extend( |
|
193 [dict(subnet_id=s) for s in dhcp_enabled_subnet_ids]) |
|
194 dhcp_port = self.plugin.update_dhcp_port( |
|
195 port.id, {'port': {'network_id': network.id, |
|
196 'fixed_ips': port_fixed_ips}}) |
|
197 if not dhcp_port: |
|
198 raise exceptions.Conflict() |
|
199 else: |
|
200 dhcp_port = port |
|
201 # break since we found port that matches device_id |
|
202 break |
230 break |
203 |
231 else: |
204 # check for a reserved DHCP port |
232 return None |
205 if dhcp_port is None: |
233 |
206 LOG.debug('DHCP port %(device_id)s on network %(network_id)s' |
234 # Compare what the subnets should be against what is already |
207 ' does not yet exist. Checking for a reserved port.', |
235 # on the port. |
208 {'device_id': device_id, 'network_id': network.id}) |
236 dhcp_enabled_subnet_ids = set(dhcp_subnets) |
209 for port in network.ports: |
237 port_subnet_ids = set(ip.subnet_id for ip in port.fixed_ips) |
210 port_device_id = getattr(port, 'device_id', None) |
238 |
211 if port_device_id == constants.DEVICE_ID_RESERVED_DHCP_PORT: |
239 # If those differ, we need to call update. |
212 dhcp_port = self.plugin.update_dhcp_port( |
240 if dhcp_enabled_subnet_ids != port_subnet_ids: |
213 port.id, {'port': {'network_id': network.id, |
241 # Collect the subnets and fixed IPs that the port already |
214 'device_id': device_id}}) |
242 # has, for subnets that are still in the DHCP-enabled set. |
215 if dhcp_port: |
243 wanted_fixed_ips = [] |
216 break |
244 for fixed_ip in port.fixed_ips: |
217 |
245 if fixed_ip.subnet_id in dhcp_enabled_subnet_ids: |
218 # DHCP port has not yet been created. |
246 wanted_fixed_ips.append( |
219 if dhcp_port is None: |
247 {'subnet_id': fixed_ip.subnet_id, |
220 LOG.debug('DHCP port %(device_id)s on network %(network_id)s' |
248 'ip_address': fixed_ip.ip_address}) |
221 ' does not yet exist.', {'device_id': device_id, |
249 |
222 'network_id': network.id}) |
250 # Add subnet IDs for new DHCP-enabled subnets. |
223 port_dict = dict( |
251 wanted_fixed_ips.extend( |
224 name='', |
252 dict(subnet_id=s) |
225 admin_state_up=True, |
253 for s in dhcp_enabled_subnet_ids - port_subnet_ids) |
226 device_id=device_id, |
254 |
227 network_id=network.id, |
255 # Update the port to have the calculated subnets and fixed |
228 tenant_id=network.tenant_id, |
256 # IPs. The Neutron server will allocate a fresh IP for |
229 fixed_ips=[dict(subnet_id=s) for s in dhcp_enabled_subnet_ids]) |
257 # each subnet that doesn't already have one. |
230 dhcp_port = self.plugin.create_dhcp_port({'port': port_dict}) |
258 port = self.plugin.update_dhcp_port( |
231 |
259 port.id, |
232 if not dhcp_port: |
260 {'port': {'network_id': network.id, |
233 raise exceptions.Conflict() |
261 'fixed_ips': wanted_fixed_ips}}) |
234 |
262 if not port: |
235 # Convert subnet_id to subnet dict |
263 raise exceptions.Conflict() |
236 fixed_ips = [dict(subnet_id=fixed_ip.subnet_id, |
264 |
237 ip_address=fixed_ip.ip_address, |
265 return port |
238 subnet=subnets[fixed_ip.subnet_id]) |
|
239 for fixed_ip in dhcp_port.fixed_ips] |
|
240 |
|
241 ips = [dhcp.DictModel(item) if isinstance(item, dict) else item |
|
242 for item in fixed_ips] |
|
243 dhcp_port.fixed_ips = ips |
|
244 |
|
245 return dhcp_port |
|
246 |
266 |
247 def setup(self, network): |
267 def setup(self, network): |
248 """Create and initialize a device for network's DHCP on this host.""" |
268 """Create and initialize a device for network's DHCP on this host.""" |
249 port = self.setup_dhcp_port(network) |
269 port = self.setup_dhcp_port(network) |
|
270 self._update_dhcp_port(network, port) |
250 interface_name = self.get_interface_name(network, port) |
271 interface_name = self.get_interface_name(network, port) |
251 |
272 |
252 if net_lib.Datalink.datalink_exists(interface_name): |
273 if net_lib.Datalink.datalink_exists(interface_name): |
253 LOG.debug('Reusing existing device: %s.', interface_name) |
274 LOG.debug('Reusing existing device: %s.', interface_name) |
254 else: |
275 else: |
255 self.driver.plug(network.tenant_id, network.id, |
276 try: |
256 port.id, interface_name, port.mac_address, |
277 self.driver.plug(network.tenant_id, network.id, |
257 network=network, |
278 port.id, interface_name, port.mac_address, |
258 vif_type=getattr(port, 'binding:vif_type', None)) |
279 network=network, mtu=network.get('mtu'), |
|
280 vif_type=getattr(port, 'binding:vif_type', |
|
281 None)) |
|
282 except Exception: |
|
283 with excutils.save_and_reraise_exception(): |
|
284 LOG.exception(_LE('Unable to plug DHCP port for ' |
|
285 'network %s. Releasing port.'), |
|
286 network.id) |
|
287 self.plugin.release_dhcp_port(network.id, port.device_id) |
259 ip_cidrs = [] |
288 ip_cidrs = [] |
260 addrconf = False |
289 addrconf = False |
261 for fixed_ip in port.fixed_ips: |
290 for fixed_ip in port.fixed_ips: |
262 subnet = fixed_ip.subnet |
291 subnet = fixed_ip.subnet |
263 if not ipv6_utils.is_auto_address_subnet(subnet): |
292 if not ipv6_utils.is_auto_address_subnet(subnet): |