blob: 27ad67e14bf258f261fc246313f12652689bf880 [file] [log] [blame]
Matteo Scandolof0441032017-08-08 13:05:26 -07001
2# Copyright 2017-present Open Networking Foundation
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16
Scott Bakerb63ea792016-08-11 10:24:48 -070017import os
18import base64
Scott Baker8b75e852016-08-16 15:04:59 -070019from synchronizers.openstack.openstacksyncstep import OpenStackSyncStep
Scott Bakerb63ea792016-08-11 10:24:48 -070020from xos.logger import observer_logger as logger
Scott Bakeraf599eb2017-03-21 12:43:26 -070021from synchronizers.new_base.modelaccessor import *
Scott Bakerb63ea792016-08-11 10:24:48 -070022
23class SyncPorts(OpenStackSyncStep):
24 requested_interval = 0 # 3600
25 provides=[Port]
26 observes=Port
27
28 # The way it works is to enumerate the all of the ports that neutron
29 # has, and then work backward from each port's network-id to determine
30 # which Network is associated from the port.
31
32 def call(self, failed=[], deletion=False):
33 if deletion:
34 self.delete_ports()
35 else:
36 self.sync_ports()
37
38 def get_driver(self, port):
39 # We need to use a client driver that specifies the tenant
40 # of the destination instance. Nova-compute will not connect
41 # ports to instances if the port's tenant does not match
42 # the instance's tenant.
43
44 # A bunch of stuff to compensate for OpenStackDriver.client_driver()
45 # not being in working condition.
Scott Baker04a37f52016-08-11 10:52:21 -070046 from synchronizers.openstack.client import OpenStackClient
47 from synchronizers.openstack.driver import OpenStackDriver
Scott Bakerb63ea792016-08-11 10:24:48 -070048 controller = port.instance.node.site_deployment.controller
49 slice = port.instance.slice
50 caller = port.network.owner.creator
51 auth = {'username': caller.email,
52 'password': caller.remote_password,
53 'tenant': slice.name}
54 client = OpenStackClient(controller=controller, **auth)
55 driver = OpenStackDriver(client=client)
56
57 return driver
58
59 def sync_ports(self):
60 logger.info("sync'ing Ports [delete=False]")
61
62 ports = Port.objects.all()
63 ports_by_id = {}
64 ports_by_neutron_port = {}
65 for port in ports:
66 ports_by_id[port.id] = port
67 ports_by_neutron_port[port.port_id] = port
68
69 networks = Network.objects.all()
70 networks_by_id = {}
71 for network in networks:
72 for nd in network.controllernetworks.all():
73 networks_by_id[nd.net_id] = network
74
75 #logger.info("networks_by_id = ")
76 #for (network_id, network) in networks_by_id.items():
77 # logger.info(" %s: %s" % (network_id, network.name))
78
79 instances = Instance.objects.all()
80 instances_by_instance_uuid = {}
81 for instance in instances:
82 instances_by_instance_uuid[instance.instance_uuid] = instance
83
84 # Get all ports in all controllers
85
86 ports_by_id = {}
87 templates_by_id = {}
88 for controller in Controller.objects.all():
89 if not controller.admin_tenant:
90 logger.info("controller %s has no admin_tenant" % controller)
91 continue
92 try:
93 driver = self.driver.admin_driver(controller = controller)
94 ports = driver.shell.neutron.list_ports()["ports"]
95 except:
96 logger.log_exc("failed to get ports from controller %s" % controller)
97 continue
98
99 for port in ports:
100 ports_by_id[port["id"]] = port
101
102 # public-nat and public-dedicated networks don't have a net-id anywhere
103 # in the data model, so build up a list of which ids map to which network
104 # templates.
105 try:
106 neutron_networks = driver.shell.neutron.list_networks()["networks"]
107 except:
108 print "failed to get networks from controller %s" % controller
109 continue
110 for network in neutron_networks:
111 for template in NetworkTemplate.objects.all():
112 if template.shared_network_name == network["name"]:
113 templates_by_id[network["id"]] = template
114
115 for port in ports_by_id.values():
116 #logger.info("port %s" % str(port))
117 if port["id"] in ports_by_neutron_port:
118 # we already have it
119 #logger.info("already accounted for port %s" % port["id"])
120 continue
121
122 if port["device_owner"] != "compute:nova":
123 # we only want the ports that connect to instances
124 #logger.info("port %s is not a compute port, it is a %s" % (port["id"], port["device_owner"]))
125 continue
126
127 instance = instances_by_instance_uuid.get(port['device_id'], None)
128 if not instance:
129 logger.info("no instance for port %s device_id %s" % (port["id"], port['device_id']))
130 continue
131
132 network = networks_by_id.get(port['network_id'], None)
133 if not network:
134 # maybe it's public-nat or public-dedicated. Search the templates for
135 # the id, then see if the instance's slice has some network that uses
136 # that template
137 template = templates_by_id.get(port['network_id'], None)
138 if template and instance.slice:
139 for candidate_network in instance.slice.networks.all():
140 if candidate_network.template == template:
141 network=candidate_network
142 if not network:
143 logger.info("no network for port %s network %s" % (port["id"], port["network_id"]))
144
145 # we know it's associated with a instance, but we don't know
146 # which network it is part of.
147
148 continue
149
150 if network.template.shared_network_name:
151 # If it's a shared network template, then more than one network
152 # object maps to the neutron network. We have to do a whole bunch
153 # of extra work to find the right one.
154 networks = network.template.network_set.all()
155 network = None
156 for candidate_network in networks:
157 if (candidate_network.owner == instance.slice):
158 logger.info("found network %s" % candidate_network)
159 network = candidate_network
160
161 if not network:
162 logger.info("failed to find the correct network for a shared template for port %s network %s" % (port["id"], port["network_id"]))
163 continue
164
165 if not port["fixed_ips"]:
166 logger.info("port %s has no fixed_ips" % port["id"])
167 continue
168
169 ip=port["fixed_ips"][0]["ip_address"]
170 mac=port["mac_address"]
171 logger.info("creating Port (%s, %s, %s, %s)" % (str(network), str(instance), ip, str(port["id"])))
172
173 ns = Port(network=network,
174 instance=instance,
175 ip=ip,
176 mac=mac,
177 port_id=port["id"])
178
179 try:
180 ns.save()
181 except:
182 logger.log_exc("failed to save port %s" % str(ns))
183 continue
184
185 # For ports that were created by the user, find that ones
Scott Bakeraf599eb2017-03-21 12:43:26 -0700186 # that don't have neutron ports, and create them. These are ports
187 # with a null port_id and a non-null instance_id.
188 ports = Port.objects.all()
189 ports = [x for x in ports if ((not x.port_id) and (x.instance_id))]
190 for port in ports:
Scott Bakerb63ea792016-08-11 10:24:48 -0700191 logger.info("XXX working on port %s" % port)
192 controller = port.instance.node.site_deployment.controller
193 slice = port.instance.slice
194
195 if controller:
Scott Bakeraf599eb2017-03-21 12:43:26 -0700196 cn=[x for x in port.network.controllernetworks.all() if x.controller_id==controller.id]
Scott Bakerb63ea792016-08-11 10:24:48 -0700197 if not cn:
198 logger.log_exc("no controllernetwork for %s" % port)
199 continue
200 cn=cn[0]
201 if cn.lazy_blocked:
202 cn.lazy_blocked=False
203 cn.save()
204 logger.info("deferring port %s because controllerNetwork was lazy-blocked" % port)
205 continue
206 if not cn.net_id:
207 logger.info("deferring port %s because controllerNetwork does not have a port-id yet" % port)
208 continue
209 try:
210 driver = self.get_driver(port)
211
212 args = {"network_id": cn.net_id}
213 neutron_port_name = port.get_parameters().get("neutron_port_name", None)
JianHao0900a272017-02-08 09:18:14 +0000214 neutron_port_ip = port.get_parameters().get("neutron_port_ip", None)
Scott Bakerb63ea792016-08-11 10:24:48 -0700215 if neutron_port_name:
216 args["name"] = neutron_port_name
JianHao0900a272017-02-08 09:18:14 +0000217 if neutron_port_ip:
218 args["fixed_ips"] = [{"ip_address": neutron_port_ip, "subnet_id": cn.subnet_id}]
Scott Bakerb63ea792016-08-11 10:24:48 -0700219
220 neutron_port = driver.shell.neutron.create_port({"port": args})["port"]
221 port.port_id = neutron_port["id"]
222 if neutron_port["fixed_ips"]:
223 port.ip = neutron_port["fixed_ips"][0]["ip_address"]
224 port.mac = neutron_port["mac_address"]
225 port.xos_created = True
226 logger.info("created neutron port %s for %s" % (port.port_id, port))
227 except:
228 logger.log_exc("failed to create neutron port for %s" % port)
229 continue
230 port.save()
231
232 def delete_ports(self):
233 logger.info("sync'ing Ports [delete=True]")
Scott Baker8c542842017-03-22 15:45:44 -0700234 ports = self.fetch_pending(deletion=True)
235 for port in ports:
Scott Bakerb63ea792016-08-11 10:24:48 -0700236 self.delete_record(port)
237
238 def delete_record(self, port):
239 if port.xos_created and port.port_id:
240 logger.info("calling openstack to destroy port %s" % port.port_id)
241 try:
242 driver = self.get_driver(port)
243 driver.shell.neutron.delete_port(port.port_id)
244 except:
245 logger.log_exc("failed to delete port %s from neutron" % port.port_id)
246 return
247
248 logger.info("Purging port %s" % port)
249 port.delete(purge=True)
250