14 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
14 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
15 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
15 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
16 # License for the specific language governing permissions and limitations |
16 # License for the specific language governing permissions and limitations |
17 # under the License. |
17 # under the License. |
18 # |
18 # |
19 # @author: Dan Wendlandt, Nicira, Inc |
|
20 # @author: Girish Moodalbail, Oracle, Inc. |
|
21 # |
|
22 |
19 |
23 """ |
20 """ |
24 Based off generic l3_agent (neutron/agent/l3_agent) code |
21 Based off generic l3_agent (neutron/agent/l3_agent) code |
25 """ |
22 """ |
26 |
23 |
|
24 import errno |
27 import netaddr |
25 import netaddr |
28 |
26 |
29 from oslo.config import cfg |
27 from oslo.config import cfg |
30 |
28 |
|
29 from neutron.agent.common import config |
31 from neutron.agent import l3_agent |
30 from neutron.agent import l3_agent |
32 from neutron.agent.linux import utils |
31 from neutron.agent.linux import utils |
33 from neutron.agent.solaris import interface |
32 from neutron.agent.solaris import interface |
34 from neutron.agent.solaris import ipfilters_manager |
|
35 from neutron.agent.solaris import net_lib |
33 from neutron.agent.solaris import net_lib |
|
34 from neutron.agent.solaris import ra |
36 from neutron.common import constants as l3_constants |
35 from neutron.common import constants as l3_constants |
|
36 from neutron.common import utils as common_utils |
37 from neutron.openstack.common import log as logging |
37 from neutron.openstack.common import log as logging |
38 |
38 |
39 |
39 |
40 LOG = logging.getLogger(__name__) |
40 LOG = logging.getLogger(__name__) |
41 INTERNAL_DEV_PREFIX = 'l3i' |
41 INTERNAL_DEV_PREFIX = 'l3i' |
42 EXTERNAL_DEV_PREFIX = 'l3e' |
42 EXTERNAL_DEV_PREFIX = 'l3e' |
43 FLOATING_IP_CIDR_SUFFIX = '/32' |
43 FLOATING_IP_CIDR_SUFFIX = '/32' |
44 |
44 |
45 |
45 |
46 class RouterInfo(object): |
46 class EVSL3NATAgent(l3_agent.L3NATAgentWithStateReport): |
47 |
|
48 def __init__(self, router_id, root_helper, use_namespaces, router): |
|
49 self.router_id = router_id |
|
50 self.ex_gw_port = None |
|
51 self._snat_enabled = None |
|
52 self._snat_action = None |
|
53 self.internal_ports = [] |
|
54 # We do not need either root_helper or namespace, so set them to None |
|
55 self.root_helper = None |
|
56 self.use_namespaces = None |
|
57 # Invoke the setter for establishing initial SNAT action |
|
58 self.router = router |
|
59 self.ipfilters_manager = ipfilters_manager.IPfiltersManager() |
|
60 self.routes = [] |
|
61 |
|
62 @property |
|
63 def router(self): |
|
64 return self._router |
|
65 |
|
66 @router.setter |
|
67 def router(self, value): |
|
68 self._router = value |
|
69 if not self._router: |
|
70 return |
|
71 # enable_snat by default if it wasn't specified by plugin |
|
72 self._snat_enabled = self._router.get('enable_snat', True) |
|
73 # Set a SNAT action for the router |
|
74 if self._router.get('gw_port'): |
|
75 self._snat_action = ('add_rules' if self._snat_enabled |
|
76 else 'remove_rules') |
|
77 elif self.ex_gw_port: |
|
78 # Gateway port was removed, remove rules |
|
79 self._snat_action = 'remove_rules' |
|
80 |
|
81 def ns_name(self): |
|
82 pass |
|
83 |
|
84 def perform_snat_action(self, snat_callback, *args): |
|
85 # Process SNAT rules for attached subnets |
|
86 if self._snat_action: |
|
87 snat_callback(self, self._router.get('gw_port'), |
|
88 *args, action=self._snat_action) |
|
89 self._snat_action = None |
|
90 |
|
91 |
|
92 class EVSL3NATAgent(l3_agent.L3NATAgent): |
|
93 |
|
94 RouterInfo = RouterInfo |
|
95 |
|
96 OPTS = [ |
47 OPTS = [ |
97 cfg.StrOpt('external_network_datalink', default='net0', |
48 cfg.StrOpt('external_network_datalink', default='net0', |
98 help=_("Name of the datalink that connects to " |
49 help=_("Name of the datalink that connects to " |
99 "an external network.")), |
50 "an external network.")), |
100 cfg.BoolOpt('allow_forwarding_between_networks', default=False, |
51 cfg.BoolOpt('allow_forwarding_between_networks', default=False, |
106 cfg.CONF.register_opts(self.OPTS) |
57 cfg.CONF.register_opts(self.OPTS) |
107 cfg.CONF.register_opts(interface.OPTS) |
58 cfg.CONF.register_opts(interface.OPTS) |
108 super(EVSL3NATAgent, self).__init__(host=host, conf=conf) |
59 super(EVSL3NATAgent, self).__init__(host=host, conf=conf) |
109 |
60 |
110 def _router_added(self, router_id, router): |
61 def _router_added(self, router_id, router): |
111 ri = RouterInfo(router_id, self.root_helper, |
62 ri = l3_agent.RouterInfo(router_id, None, |
112 self.conf.use_namespaces, router) |
63 self.conf.use_namespaces, router) |
113 self.router_info[router_id] = ri |
64 self.router_info[router_id] = ri |
114 |
65 |
|
66 if self.conf.enable_metadata_proxy: |
|
67 self._spawn_metadata_proxy(ri.router_id, ri.ns_name) |
|
68 |
115 def _router_removed(self, router_id): |
69 def _router_removed(self, router_id): |
116 ri = self.router_info[router_id] |
70 ri = self.router_info.get(router_id) |
|
71 if ri is None: |
|
72 LOG.warn(_("Info for router %s were not found. " |
|
73 "Skipping router removal"), router_id) |
|
74 return |
117 ri.router['gw_port'] = None |
75 ri.router['gw_port'] = None |
118 ri.router[l3_constants.INTERFACE_KEY] = [] |
76 ri.router[l3_constants.INTERFACE_KEY] = [] |
119 ri.router[l3_constants.FLOATINGIP_KEY] = [] |
77 ri.router[l3_constants.FLOATINGIP_KEY] = [] |
120 self.process_router(ri) |
78 self.process_router(ri) |
|
79 if self.conf.enable_metadata_proxy: |
|
80 self._destroy_metadata_proxy(ri.router_id, ri.ns_name) |
|
81 |
121 del self.router_info[router_id] |
82 del self.router_info[router_id] |
122 |
83 |
|
84 def _get_metadata_proxy_callback(self, router_id): |
|
85 """Need to override this since we need to pass the absolute |
|
86 path to neutron-ns-metadata-proxy binary. |
|
87 """ |
|
88 def callback(pid_file): |
|
89 metadata_proxy_socket = cfg.CONF.metadata_proxy_socket |
|
90 proxy_cmd = ['/usr/lib/neutron/neutron-ns-metadata-proxy', |
|
91 '--pid_file=%s' % pid_file, |
|
92 '--metadata_proxy_socket=%s' % metadata_proxy_socket, |
|
93 '--router_id=%s' % router_id, |
|
94 '--state_path=%s' % self.conf.state_path, |
|
95 '--metadata_port=%s' % self.conf.metadata_port] |
|
96 proxy_cmd.extend(config.get_log_args( |
|
97 cfg.CONF, 'neutron-ns-metadata-proxy-%s.log' % |
|
98 router_id)) |
|
99 return proxy_cmd |
|
100 |
|
101 return callback |
|
102 |
|
103 def external_gateway_snat_rules(self, ex_gw_ip, internal_cidrs, |
|
104 interface_name): |
|
105 rules = [] |
|
106 for cidr in internal_cidrs: |
|
107 rules.append('map %s %s -> %s/32' % |
|
108 (interface_name, cidr, ex_gw_ip)) |
|
109 return rules |
|
110 |
|
111 def _handle_router_snat_rules(self, ri, ex_gw_port, internal_cidrs, |
|
112 interface_name, action): |
|
113 assert not ri.router['distributed'] |
|
114 |
|
115 # Remove all the old SNAT rules |
|
116 # This is safe because if use_namespaces is set as False |
|
117 # then the agent can only configure one router, otherwise |
|
118 # each router's SNAT rules will be in their own namespace |
|
119 |
|
120 # get only the SNAT rules |
|
121 old_snat_rules = [rule for rule in ri.ipfilters_manager.ipv4['nat'] |
|
122 if rule.startswith('map')] |
|
123 ri.ipfilters_manager.remove_nat_rules(old_snat_rules) |
|
124 |
|
125 # And add them back if the action is add_rules |
|
126 if action == 'add_rules' and ex_gw_port: |
|
127 # NAT rules are added only if ex_gw_port has an IPv4 address |
|
128 for ip_addr in ex_gw_port['fixed_ips']: |
|
129 ex_gw_ip = ip_addr['ip_address'] |
|
130 if netaddr.IPAddress(ex_gw_ip).version == 4: |
|
131 rules = self.external_gateway_snat_rules(ex_gw_ip, |
|
132 internal_cidrs, |
|
133 interface_name) |
|
134 ri.ipfilters_manager.add_nat_rules(rules) |
|
135 break |
|
136 |
|
137 @common_utils.exception_logger() |
123 def process_router(self, ri): |
138 def process_router(self, ri): |
|
139 # TODO(mrsmith) - we shouldn't need to check here |
|
140 if 'distributed' not in ri.router: |
|
141 ri.router['distributed'] = False |
124 ex_gw_port = self._get_ex_gw_port(ri) |
142 ex_gw_port = self._get_ex_gw_port(ri) |
125 internal_ports = ri.router.get(l3_constants.INTERFACE_KEY, []) |
143 internal_ports = ri.router.get(l3_constants.INTERFACE_KEY, []) |
126 existing_port_ids = set([p['id'] for p in ri.internal_ports]) |
144 existing_port_ids = set([p['id'] for p in ri.internal_ports]) |
127 current_port_ids = set([p['id'] for p in internal_ports |
145 current_port_ids = set([p['id'] for p in internal_ports |
128 if p['admin_state_up']]) |
146 if p['admin_state_up']]) |
129 new_ports = [p for p in internal_ports if |
147 new_ports = [p for p in internal_ports if |
130 p['id'] in current_port_ids and |
148 p['id'] in current_port_ids and |
131 p['id'] not in existing_port_ids] |
149 p['id'] not in existing_port_ids] |
132 old_ports = [p for p in ri.internal_ports if |
150 old_ports = [p for p in ri.internal_ports if |
133 p['id'] not in current_port_ids] |
151 p['id'] not in current_port_ids] |
|
152 new_ipv6_port = False |
|
153 old_ipv6_port = False |
134 for p in new_ports: |
154 for p in new_ports: |
135 self._set_subnet_info(p) |
155 self._set_subnet_info(p) |
|
156 self.internal_network_added(ri, p) |
136 ri.internal_ports.append(p) |
157 ri.internal_ports.append(p) |
137 self.internal_network_added(ri, p) |
158 if (not new_ipv6_port and |
|
159 netaddr.IPNetwork(p['subnet']['cidr']).version == 6): |
|
160 new_ipv6_port = True |
138 |
161 |
139 for p in old_ports: |
162 for p in old_ports: |
|
163 self.internal_network_removed(ri, p) |
140 ri.internal_ports.remove(p) |
164 ri.internal_ports.remove(p) |
141 self.internal_network_removed(ri, p) |
165 if (not old_ipv6_port and |
142 |
166 netaddr.IPNetwork(p['subnet']['cidr']).version == 6): |
143 internal_cidrs = [p['ip_cidr'] for p in ri.internal_ports] |
167 old_ipv6_port = True |
|
168 |
|
169 if new_ipv6_port or old_ipv6_port: |
|
170 # refresh ndpd daemon after filling in ndpd.conf |
|
171 # with the right entries |
|
172 ra.enable_ipv6_ra(ri.router_id, |
|
173 internal_ports, |
|
174 self.get_internal_device_name) |
|
175 |
|
176 # remove any internal stale router interfaces (i.e., l3i.. VNICs) |
|
177 existing_devices = net_lib.Datalink.show_vnic() |
|
178 current_internal_devs = set([n for n in existing_devices |
|
179 if n.startswith(INTERNAL_DEV_PREFIX)]) |
|
180 current_port_devs = set([self.get_internal_device_name(id) for |
|
181 id in current_port_ids]) |
|
182 stale_devs = current_internal_devs - current_port_devs |
|
183 for stale_dev in stale_devs: |
|
184 LOG.debug(_('Deleting stale internal router device: %s'), |
|
185 stale_dev) |
|
186 self.driver.fini_l3(stale_dev) |
|
187 self.driver.unplug(stale_dev) |
|
188 |
144 # TODO(salv-orlando): RouterInfo would be a better place for |
189 # TODO(salv-orlando): RouterInfo would be a better place for |
145 # this logic too |
190 # this logic too |
146 ex_gw_port_id = (ex_gw_port and ex_gw_port['id'] or |
191 ex_gw_port_id = (ex_gw_port and ex_gw_port['id'] or |
147 ri.ex_gw_port and ri.ex_gw_port['id']) |
192 ri.ex_gw_port and ri.ex_gw_port['id']) |
148 |
193 |
149 interface_name = None |
194 interface_name = None |
150 if ex_gw_port_id: |
195 if ex_gw_port_id: |
151 interface_name = self.get_external_device_name(ex_gw_port_id) |
196 interface_name = self.get_external_device_name(ex_gw_port_id) |
152 if ex_gw_port and not ri.ex_gw_port: |
197 if ex_gw_port: |
|
198 def _gateway_ports_equal(port1, port2): |
|
199 def _get_filtered_dict(d, ignore): |
|
200 return dict((k, v) for k, v in d.iteritems() |
|
201 if k not in ignore) |
|
202 |
|
203 keys_to_ignore = set(['binding:host_id']) |
|
204 port1_filtered = _get_filtered_dict(port1, keys_to_ignore) |
|
205 port2_filtered = _get_filtered_dict(port2, keys_to_ignore) |
|
206 return port1_filtered == port2_filtered |
|
207 |
153 self._set_subnet_info(ex_gw_port) |
208 self._set_subnet_info(ex_gw_port) |
154 self.external_gateway_added(ri, ex_gw_port, |
209 if not ri.ex_gw_port: |
155 interface_name, internal_cidrs) |
210 self.external_gateway_added(ri, ex_gw_port, interface_name) |
|
211 elif not _gateway_ports_equal(ex_gw_port, ri.ex_gw_port): |
|
212 self.external_gateway_updated(ri, ex_gw_port, interface_name) |
156 elif not ex_gw_port and ri.ex_gw_port: |
213 elif not ex_gw_port and ri.ex_gw_port: |
157 self.external_gateway_removed(ri, ri.ex_gw_port, |
214 self.external_gateway_removed(ri, ri.ex_gw_port, interface_name) |
158 interface_name, internal_cidrs) |
215 |
159 |
216 # Remove any external stale router interfaces (i.e., l3e.. VNICs) |
160 # We don't need this since our IPnat rules are bi-directional |
217 stale_devs = [dev for dev in existing_devices |
|
218 if dev.startswith(EXTERNAL_DEV_PREFIX) |
|
219 and dev != interface_name] |
|
220 for stale_dev in stale_devs: |
|
221 LOG.debug(_('Deleting stale external router device: %s'), |
|
222 stale_dev) |
|
223 self.driver.fini_l3(stale_dev) |
|
224 self.driver.unplug(stale_dev) |
|
225 |
|
226 # Process static routes for router |
|
227 self.routes_updated(ri) |
|
228 |
161 # Process SNAT rules for external gateway |
229 # Process SNAT rules for external gateway |
162 # ri.perform_snat_action(self._handle_router_snat_rules, |
230 if (not ri.router['distributed'] or |
163 # internal_cidrs, interface_name) |
231 ex_gw_port and self.get_gw_port_host(ri.router) == self.host): |
164 |
232 # Get IPv4 only internal CIDRs |
165 # Process DNAT rules for floating IPs |
233 internal_cidrs = [p['ip_cidr'] for p in ri.internal_ports |
|
234 if netaddr.IPNetwork(p['ip_cidr']).version == 4] |
|
235 ri.perform_snat_action(self._handle_router_snat_rules, |
|
236 internal_cidrs, interface_name) |
|
237 |
|
238 # Process SNAT/DNAT rules for floating IPs |
|
239 fip_statuses = {} |
166 if ex_gw_port: |
240 if ex_gw_port: |
167 self.process_router_floating_ips(ri, ex_gw_port) |
241 existing_floating_ips = ri.floating_ips |
168 |
242 fip_statuses = self.process_router_floating_ips(ri, ex_gw_port) |
|
243 # Identify floating IPs which were disabled |
|
244 ri.floating_ips = set(fip_statuses.keys()) |
|
245 for fip_id in existing_floating_ips - ri.floating_ips: |
|
246 fip_statuses[fip_id] = l3_constants.FLOATINGIP_STATUS_DOWN |
|
247 # Update floating IP status on the neutron server |
|
248 self.plugin_rpc.update_floatingip_statuses( |
|
249 self.context, ri.router_id, fip_statuses) |
|
250 |
|
251 # Update ex_gw_port and enable_snat on the router info cache |
169 ri.ex_gw_port = ex_gw_port |
252 ri.ex_gw_port = ex_gw_port |
170 ri.enable_snat = ri.router.get('enable_snat') |
253 ri.enable_snat = ri.router.get('enable_snat') |
171 self.routes_updated(ri) |
|
172 |
254 |
173 def process_router_floating_ips(self, ri, ex_gw_port): |
255 def process_router_floating_ips(self, ri, ex_gw_port): |
174 """Configure the router's floating IPs |
256 """Configure the router's floating IPs |
175 Configures floating ips using ipnat(1m) on the router's gateway device. |
257 Configures floating ips using ipnat(1m) on the router's gateway device. |
176 |
258 |
178 """ |
260 """ |
179 ifname = self.get_external_device_name(ex_gw_port['id']) |
261 ifname = self.get_external_device_name(ex_gw_port['id']) |
180 ipintf = net_lib.IPInterface(ifname) |
262 ipintf = net_lib.IPInterface(ifname) |
181 ipaddr_list = ipintf.ipaddr_list()['static'] |
263 ipaddr_list = ipintf.ipaddr_list()['static'] |
182 |
264 |
183 existing_cidrs = set([addr for addr in ipaddr_list]) |
265 existing_cidrs = set(ipaddr_list) |
184 new_cidrs = set() |
266 new_cidrs = set() |
185 |
267 |
186 existing_nat_rules = [nat_rule for nat_rule in |
268 existing_nat_rules = [nat_rule for nat_rule in |
187 ri.ipfilters_manager.ipv4['nat']] |
269 ri.ipfilters_manager.ipv4['nat']] |
188 new_nat_rules = [] |
270 new_nat_rules = [] |
189 |
271 |
190 # Loop once to ensure that floating ips are configured. |
272 # Loop once to ensure that floating ips are configured. |
|
273 fip_statuses = {} |
191 for fip in ri.router.get(l3_constants.FLOATINGIP_KEY, []): |
274 for fip in ri.router.get(l3_constants.FLOATINGIP_KEY, []): |
192 fip_ip = fip['floating_ip_address'] |
275 fip_ip = fip['floating_ip_address'] |
193 fip_cidr = str(fip_ip) + FLOATING_IP_CIDR_SUFFIX |
276 fip_cidr = str(fip_ip) + FLOATING_IP_CIDR_SUFFIX |
194 new_cidrs.add(fip_cidr) |
277 new_cidrs.add(fip_cidr) |
195 fixed_cidr = str(fip['fixed_ip_address']) + '/32' |
278 fixed_cidr = str(fip['fixed_ip_address']) + '/32' |
196 nat_rule = 'bimap %s %s -> %s' % (ifname, fixed_cidr, fip_cidr) |
279 nat_rule = 'bimap %s %s -> %s' % (ifname, fixed_cidr, fip_cidr) |
197 |
280 |
198 if fip_cidr not in existing_cidrs: |
281 if fip_cidr not in existing_cidrs: |
199 ipintf.create_address(fip_cidr) |
282 try: |
200 ri.ipfilters_manager.add_nat_rules([nat_rule]) |
283 ipintf.create_address(fip_cidr) |
|
284 ri.ipfilters_manager.add_nat_rules([nat_rule]) |
|
285 except Exception as err: |
|
286 # TODO(gmoodalb): If we fail in add_nat_rules(), then |
|
287 # we need to remove the fip_cidr address |
|
288 |
|
289 # any exception occurred here should cause the floating IP |
|
290 # to be set in error state |
|
291 fip_statuses[fip['id']] = ( |
|
292 l3_constants.FLOATINGIP_STATUS_ERROR) |
|
293 LOG.warn(_("Unable to configure IP address for " |
|
294 "floating IP: %s: %s") % (fip['id'], err)) |
|
295 continue |
|
296 fip_statuses[fip['id']] = ( |
|
297 l3_constants.FLOATINGIP_STATUS_ACTIVE) |
201 new_nat_rules.append(nat_rule) |
298 new_nat_rules.append(nat_rule) |
202 |
299 |
203 # remove all the old NAT rules |
300 # remove all the old NAT rules |
204 ri.ipfilters_manager.remove_nat_rules(list(set(existing_nat_rules) - |
301 old_nat_rules = list(set(existing_nat_rules) - set(new_nat_rules)) |
205 set(new_nat_rules))) |
302 # Filter out 'bimap' NAT rules as we don't want to remove NAT rules |
|
303 # that were added for Metadata server |
|
304 old_nat_rules = [rule for rule in old_nat_rules if "bimap" in rule] |
|
305 ri.ipfilters_manager.remove_nat_rules(old_nat_rules) |
206 |
306 |
207 # Clean up addresses that no longer belong on the gateway interface. |
307 # Clean up addresses that no longer belong on the gateway interface. |
208 for ip_cidr in existing_cidrs - new_cidrs: |
308 for ip_cidr in existing_cidrs - new_cidrs: |
209 if ip_cidr.endswith(FLOATING_IP_CIDR_SUFFIX): |
309 if ip_cidr.endswith(FLOATING_IP_CIDR_SUFFIX): |
210 ipintf.delete_address(ip_cidr) |
310 ipintf.delete_address(ip_cidr) |
|
311 return fip_statuses |
211 |
312 |
212 def get_internal_device_name(self, port_id): |
313 def get_internal_device_name(self, port_id): |
213 # Because of the way how dnsmasq works on Solaris, the length |
314 # Because of the way how dnsmasq works on Solaris, the length |
214 # of datalink name cannot exceed 16 (includes terminating nul |
315 # of datalink name cannot exceed 16 (includes terminating nul |
215 # character). So, the linkname can only have 15 characters and |
316 # character). So, the linkname can only have 15 characters and |
223 # please see the comment above |
324 # please see the comment above |
224 dname = (EXTERNAL_DEV_PREFIX + port_id)[:13] |
325 dname = (EXTERNAL_DEV_PREFIX + port_id)[:13] |
225 dname += '_0' |
326 dname += '_0' |
226 return dname.replace('-', '_') |
327 return dname.replace('-', '_') |
227 |
328 |
228 def external_gateway_added(self, ri, ex_gw_port, |
329 def external_gateway_added(self, ri, ex_gw_port, external_dlname): |
229 external_dlname, internal_cidrs): |
|
230 |
330 |
231 if not net_lib.Datalink.datalink_exists(external_dlname): |
331 if not net_lib.Datalink.datalink_exists(external_dlname): |
232 dl = net_lib.Datalink(external_dlname) |
332 dl = net_lib.Datalink(external_dlname) |
233 # need to determine the VLAN ID for the VNIC |
333 # determine the network type of the external network |
234 evsname = ex_gw_port['network_id'] |
334 evsname = ex_gw_port['network_id'] |
235 tenantname = ex_gw_port['tenant_id'] |
335 cmd = ['/usr/sbin/evsadm', 'show-evs', '-co', 'l2type,vid', |
236 cmd = ['/usr/sbin/evsadm', 'show-evs', '-co', 'vid', |
336 '-f', 'evs=%s' % evsname] |
237 '-f', 'tenant=%s' % tenantname, evsname] |
|
238 try: |
337 try: |
239 stdout = utils.execute(cmd) |
338 stdout = utils.execute(cmd) |
240 except Exception as err: |
339 except Exception as err: |
241 LOG.error(_("Failed to retrieve the VLAN ID associated " |
340 LOG.error(_("Failed to retrieve the network type for " |
242 "with the external network, and it is required " |
341 "the external network, and it is required " |
243 "to create external gateway port: %s") % err) |
342 "to create an external gateway port: %s") % err) |
244 return |
343 return |
245 vid = stdout.splitlines()[0].strip() |
344 output = stdout.splitlines()[0].strip() |
246 if vid == "": |
345 l2type, vid = output.split(':') |
247 LOG.error(_("External Network does not have a VLAN ID " |
346 if l2type != 'flat' and l2type != 'vlan': |
248 "associated with it, and it is required to " |
347 LOG.error(_("External network should be either Flat or " |
249 "create external gateway port")) |
348 "VLAN based, and it is required to " |
|
349 "create an external gateway port")) |
250 return |
350 return |
251 mac_address = ex_gw_port['mac_address'] |
351 elif (l2type == 'vlan' and |
252 dl.create_vnic(self.conf.external_network_datalink, |
352 self.conf.get("external_network_datalink", None)): |
253 mac_address=mac_address, vid=vid) |
353 LOG.warning(_("external_network_datalink is deprecated in " |
|
354 "Juno and will be removed in the next release of " |
|
355 "Solaris OpenStack. Please use the evsadm " |
|
356 "set-controlprop subcommand to setup the " |
|
357 "uplink-port for an external network")) |
|
358 # proceed with the old-style of doing things |
|
359 mac_address = ex_gw_port['mac_address'] |
|
360 dl.create_vnic(self.conf.external_network_datalink, |
|
361 mac_address=mac_address, vid=vid) |
|
362 else: |
|
363 # This is to handle HA by Solaris Cluster and is similar to |
|
364 # the code we already have for the DHCP Agent. So, when |
|
365 # the 1st L3 agent is down and the second L3 agent tries to |
|
366 # connect its VNIC to EVS, we will end up in "vport in use" |
|
367 # error. So, we need to reset the vport before we connect |
|
368 # the VNIC to EVS. |
|
369 cmd = ['/usr/sbin/evsadm', 'show-vport', '-f', |
|
370 'vport=%s' % ex_gw_port['id'], '-co', |
|
371 'evs,vport,status'] |
|
372 stdout = utils.execute(cmd) |
|
373 evsname, vportname, status = stdout.strip().split(':') |
|
374 tenant_id = ex_gw_port['tenant_id'] |
|
375 if status == 'used': |
|
376 cmd = ['/usr/sbin/evsadm', 'reset-vport', '-T', tenant_id, |
|
377 '%s/%s' % (evsname, vportname)] |
|
378 utils.execute(cmd) |
|
379 |
|
380 # next remove protection setting on the VPort to allow |
|
381 # multiple floating IPs to be configured on the l3e* |
|
382 # interface |
|
383 evsvport = "%s/%s" % (ex_gw_port['network_id'], |
|
384 ex_gw_port['id']) |
|
385 cmd = ['/usr/sbin/evsadm', 'set-vportprop', '-T', |
|
386 tenant_id, '-p', 'protection=none', evsvport] |
|
387 utils.execute(cmd) |
|
388 dl.connect_vnic(evsvport, tenant_id) |
|
389 |
254 self.driver.init_l3(external_dlname, [ex_gw_port['ip_cidr']]) |
390 self.driver.init_l3(external_dlname, [ex_gw_port['ip_cidr']]) |
255 |
391 |
256 # TODO(gmoodalb): wrap route(1m) command within a class in net_lib.py |
392 # TODO(gmoodalb): wrap route(1m) command within a class in net_lib.py |
257 gw_ip = ex_gw_port['subnet']['gateway_ip'] |
393 gw_ip = ex_gw_port['subnet']['gateway_ip'] |
258 if gw_ip: |
394 if gw_ip: |
259 cmd = ['/usr/bin/pfexec', '/usr/sbin/route', 'add', 'default', |
395 cmd = ['/usr/bin/pfexec', '/usr/sbin/route', 'add', 'default', |
260 gw_ip] |
396 gw_ip] |
261 utils.execute(cmd, check_exit_code=False) |
397 stdout = utils.execute(cmd, extra_ok_codes=[errno.EEXIST]) |
|
398 ri.remove_route = True |
|
399 if 'entry exists' in stdout: |
|
400 ri.remove_route = False |
262 |
401 |
263 # for each of the internal ports, add Policy Based |
402 # for each of the internal ports, add Policy Based |
264 # Routing (PBR) rule |
403 # Routing (PBR) rule |
265 for port in ri.internal_ports: |
404 for port in ri.internal_ports: |
266 internal_dlname = self.get_internal_device_name(port['id']) |
405 internal_dlname = self.get_internal_device_name(port['id']) |