blob: 03b01335e6d758785d3355df1b0d2ceec81a5bd9 [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
Scott Bakerc808c672019-02-04 11:38:20 -080016from openstacksyncstep import OpenStackSyncStep
17from xossynchronizer.modelaccessor import *
18from xosconfig import Config
19from multistructlog import create_logger
Matteo Scandolof0441032017-08-08 13:05:26 -070020
Scott Bakerc808c672019-02-04 11:38:20 -080021log = create_logger(Config().get('logging'))
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):
Scott Bakerc808c672019-02-04 11:38:20 -080060 log.info("sync'ing Ports [delete=False]")
Scott Bakerb63ea792016-08-11 10:24:48 -070061
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
Scott Bakerb63ea792016-08-11 10:24:48 -070075 instances = Instance.objects.all()
76 instances_by_instance_uuid = {}
77 for instance in instances:
78 instances_by_instance_uuid[instance.instance_uuid] = instance
79
80 # Get all ports in all controllers
81
82 ports_by_id = {}
83 templates_by_id = {}
84 for controller in Controller.objects.all():
85 if not controller.admin_tenant:
Scott Bakerc808c672019-02-04 11:38:20 -080086 log.info("controller %s has no admin_tenant" % controller)
Scott Bakerb63ea792016-08-11 10:24:48 -070087 continue
88 try:
89 driver = self.driver.admin_driver(controller = controller)
90 ports = driver.shell.neutron.list_ports()["ports"]
91 except:
Scott Bakerc808c672019-02-04 11:38:20 -080092 log.exception("failed to get ports from controller %s" % controller)
Scott Bakerb63ea792016-08-11 10:24:48 -070093 continue
94
95 for port in ports:
96 ports_by_id[port["id"]] = port
97
98 # public-nat and public-dedicated networks don't have a net-id anywhere
99 # in the data model, so build up a list of which ids map to which network
100 # templates.
101 try:
102 neutron_networks = driver.shell.neutron.list_networks()["networks"]
103 except:
104 print "failed to get networks from controller %s" % controller
105 continue
106 for network in neutron_networks:
107 for template in NetworkTemplate.objects.all():
108 if template.shared_network_name == network["name"]:
109 templates_by_id[network["id"]] = template
110
111 for port in ports_by_id.values():
Scott Bakerb63ea792016-08-11 10:24:48 -0700112 if port["id"] in ports_by_neutron_port:
113 # we already have it
114 #logger.info("already accounted for port %s" % port["id"])
115 continue
116
117 if port["device_owner"] != "compute:nova":
118 # we only want the ports that connect to instances
119 #logger.info("port %s is not a compute port, it is a %s" % (port["id"], port["device_owner"]))
120 continue
121
122 instance = instances_by_instance_uuid.get(port['device_id'], None)
123 if not instance:
Scott Bakerc808c672019-02-04 11:38:20 -0800124 log.info("no instance for port %s device_id %s" % (port["id"], port['device_id']))
Scott Bakerb63ea792016-08-11 10:24:48 -0700125 continue
126
127 network = networks_by_id.get(port['network_id'], None)
128 if not network:
129 # maybe it's public-nat or public-dedicated. Search the templates for
130 # the id, then see if the instance's slice has some network that uses
131 # that template
132 template = templates_by_id.get(port['network_id'], None)
133 if template and instance.slice:
134 for candidate_network in instance.slice.networks.all():
135 if candidate_network.template == template:
136 network=candidate_network
137 if not network:
Scott Bakerc808c672019-02-04 11:38:20 -0800138 log.info("no network for port %s network %s" % (port["id"], port["network_id"]))
Scott Bakerb63ea792016-08-11 10:24:48 -0700139
140 # we know it's associated with a instance, but we don't know
141 # which network it is part of.
142
143 continue
144
145 if network.template.shared_network_name:
146 # If it's a shared network template, then more than one network
147 # object maps to the neutron network. We have to do a whole bunch
148 # of extra work to find the right one.
149 networks = network.template.network_set.all()
150 network = None
151 for candidate_network in networks:
152 if (candidate_network.owner == instance.slice):
Scott Bakerc808c672019-02-04 11:38:20 -0800153 log.info("found network %s" % candidate_network)
Scott Bakerb63ea792016-08-11 10:24:48 -0700154 network = candidate_network
155
156 if not network:
Scott Bakerc808c672019-02-04 11:38:20 -0800157 log.info("failed to find the correct network for a shared template for port %s network %s" % (port["id"], port["network_id"]))
Scott Bakerb63ea792016-08-11 10:24:48 -0700158 continue
159
160 if not port["fixed_ips"]:
Scott Bakerc808c672019-02-04 11:38:20 -0800161 log.info("port %s has no fixed_ips" % port["id"])
Scott Bakerb63ea792016-08-11 10:24:48 -0700162 continue
163
164 ip=port["fixed_ips"][0]["ip_address"]
165 mac=port["mac_address"]
Scott Bakerc808c672019-02-04 11:38:20 -0800166 log.info("creating Port (%s, %s, %s, %s)" % (str(network), str(instance), ip, str(port["id"])))
Scott Bakerb63ea792016-08-11 10:24:48 -0700167
168 ns = Port(network=network,
169 instance=instance,
170 ip=ip,
171 mac=mac,
172 port_id=port["id"])
173
174 try:
175 ns.save()
176 except:
Scott Bakerc808c672019-02-04 11:38:20 -0800177 log.exception("failed to save port %s" % str(ns))
Scott Bakerb63ea792016-08-11 10:24:48 -0700178 continue
179
180 # For ports that were created by the user, find that ones
Scott Bakeraf599eb2017-03-21 12:43:26 -0700181 # that don't have neutron ports, and create them. These are ports
182 # with a null port_id and a non-null instance_id.
183 ports = Port.objects.all()
184 ports = [x for x in ports if ((not x.port_id) and (x.instance_id))]
185 for port in ports:
Scott Bakerc808c672019-02-04 11:38:20 -0800186 log.info("XXX working on port %s" % port)
Scott Bakerb63ea792016-08-11 10:24:48 -0700187 controller = port.instance.node.site_deployment.controller
188 slice = port.instance.slice
189
190 if controller:
Scott Bakeraf599eb2017-03-21 12:43:26 -0700191 cn=[x for x in port.network.controllernetworks.all() if x.controller_id==controller.id]
Scott Bakerb63ea792016-08-11 10:24:48 -0700192 if not cn:
Scott Bakerc808c672019-02-04 11:38:20 -0800193 log.exception("no controllernetwork for %s" % port)
Scott Bakerb63ea792016-08-11 10:24:48 -0700194 continue
195 cn=cn[0]
196 if cn.lazy_blocked:
197 cn.lazy_blocked=False
198 cn.save()
Scott Bakerc808c672019-02-04 11:38:20 -0800199 log.info("deferring port %s because controllerNetwork was lazy-blocked" % port)
Scott Bakerb63ea792016-08-11 10:24:48 -0700200 continue
201 if not cn.net_id:
Scott Bakerc808c672019-02-04 11:38:20 -0800202 log.info("deferring port %s because controllerNetwork does not have a port-id yet" % port)
Scott Bakerb63ea792016-08-11 10:24:48 -0700203 continue
204 try:
205 driver = self.get_driver(port)
206
207 args = {"network_id": cn.net_id}
208 neutron_port_name = port.get_parameters().get("neutron_port_name", None)
JianHao0900a272017-02-08 09:18:14 +0000209 neutron_port_ip = port.get_parameters().get("neutron_port_ip", None)
Scott Bakerb63ea792016-08-11 10:24:48 -0700210 if neutron_port_name:
211 args["name"] = neutron_port_name
JianHao0900a272017-02-08 09:18:14 +0000212 if neutron_port_ip:
213 args["fixed_ips"] = [{"ip_address": neutron_port_ip, "subnet_id": cn.subnet_id}]
Scott Bakerb63ea792016-08-11 10:24:48 -0700214
215 neutron_port = driver.shell.neutron.create_port({"port": args})["port"]
216 port.port_id = neutron_port["id"]
217 if neutron_port["fixed_ips"]:
218 port.ip = neutron_port["fixed_ips"][0]["ip_address"]
219 port.mac = neutron_port["mac_address"]
220 port.xos_created = True
Scott Bakerc808c672019-02-04 11:38:20 -0800221 log.info("created neutron port %s for %s" % (port.port_id, port))
Scott Bakerb63ea792016-08-11 10:24:48 -0700222 except:
Scott Bakerc808c672019-02-04 11:38:20 -0800223 log.exception("failed to create neutron port for %s" % port)
Scott Bakerb63ea792016-08-11 10:24:48 -0700224 continue
225 port.save()
226
227 def delete_ports(self):
Scott Bakerc808c672019-02-04 11:38:20 -0800228 log.info("sync'ing Ports [delete=True]")
Scott Baker8c542842017-03-22 15:45:44 -0700229 ports = self.fetch_pending(deletion=True)
230 for port in ports:
Scott Bakerb63ea792016-08-11 10:24:48 -0700231 self.delete_record(port)
232
233 def delete_record(self, port):
234 if port.xos_created and port.port_id:
Scott Bakerc808c672019-02-04 11:38:20 -0800235 log.info("calling openstack to destroy port %s" % port.port_id)
Scott Bakerb63ea792016-08-11 10:24:48 -0700236 try:
237 driver = self.get_driver(port)
238 driver.shell.neutron.delete_port(port.port_id)
239 except:
Scott Bakerc808c672019-02-04 11:38:20 -0800240 log.exception("failed to delete port %s from neutron" % port.port_id)
Scott Bakerb63ea792016-08-11 10:24:48 -0700241 return
242
Scott Bakerc808c672019-02-04 11:38:20 -0800243 log.info("Purging port %s" % port)
Scott Bakerb63ea792016-08-11 10:24:48 -0700244 port.delete(purge=True)
245