blob: f8a9d0b9809f9fb06491893354c60c8125e9f40d [file] [log] [blame]
Scott Bakerb63ea792016-08-11 10:24:48 -07001import os
2import base64
Scott Baker8b75e852016-08-16 15:04:59 -07003from synchronizers.openstack.openstacksyncstep import OpenStackSyncStep
Scott Bakerb63ea792016-08-11 10:24:48 -07004from xos.logger import observer_logger as logger
Scott Bakeraf599eb2017-03-21 12:43:26 -07005from synchronizers.new_base.modelaccessor import *
Scott Bakerb63ea792016-08-11 10:24:48 -07006
7class SyncPorts(OpenStackSyncStep):
8 requested_interval = 0 # 3600
9 provides=[Port]
10 observes=Port
11
12 # The way it works is to enumerate the all of the ports that neutron
13 # has, and then work backward from each port's network-id to determine
14 # which Network is associated from the port.
15
16 def call(self, failed=[], deletion=False):
17 if deletion:
18 self.delete_ports()
19 else:
20 self.sync_ports()
21
22 def get_driver(self, port):
23 # We need to use a client driver that specifies the tenant
24 # of the destination instance. Nova-compute will not connect
25 # ports to instances if the port's tenant does not match
26 # the instance's tenant.
27
28 # A bunch of stuff to compensate for OpenStackDriver.client_driver()
29 # not being in working condition.
Scott Baker04a37f52016-08-11 10:52:21 -070030 from synchronizers.openstack.client import OpenStackClient
31 from synchronizers.openstack.driver import OpenStackDriver
Scott Bakerb63ea792016-08-11 10:24:48 -070032 controller = port.instance.node.site_deployment.controller
33 slice = port.instance.slice
34 caller = port.network.owner.creator
35 auth = {'username': caller.email,
36 'password': caller.remote_password,
37 'tenant': slice.name}
38 client = OpenStackClient(controller=controller, **auth)
39 driver = OpenStackDriver(client=client)
40
41 return driver
42
43 def sync_ports(self):
44 logger.info("sync'ing Ports [delete=False]")
45
46 ports = Port.objects.all()
47 ports_by_id = {}
48 ports_by_neutron_port = {}
49 for port in ports:
50 ports_by_id[port.id] = port
51 ports_by_neutron_port[port.port_id] = port
52
53 networks = Network.objects.all()
54 networks_by_id = {}
55 for network in networks:
56 for nd in network.controllernetworks.all():
57 networks_by_id[nd.net_id] = network
58
59 #logger.info("networks_by_id = ")
60 #for (network_id, network) in networks_by_id.items():
61 # logger.info(" %s: %s" % (network_id, network.name))
62
63 instances = Instance.objects.all()
64 instances_by_instance_uuid = {}
65 for instance in instances:
66 instances_by_instance_uuid[instance.instance_uuid] = instance
67
68 # Get all ports in all controllers
69
70 ports_by_id = {}
71 templates_by_id = {}
72 for controller in Controller.objects.all():
73 if not controller.admin_tenant:
74 logger.info("controller %s has no admin_tenant" % controller)
75 continue
76 try:
77 driver = self.driver.admin_driver(controller = controller)
78 ports = driver.shell.neutron.list_ports()["ports"]
79 except:
80 logger.log_exc("failed to get ports from controller %s" % controller)
81 continue
82
83 for port in ports:
84 ports_by_id[port["id"]] = port
85
86 # public-nat and public-dedicated networks don't have a net-id anywhere
87 # in the data model, so build up a list of which ids map to which network
88 # templates.
89 try:
90 neutron_networks = driver.shell.neutron.list_networks()["networks"]
91 except:
92 print "failed to get networks from controller %s" % controller
93 continue
94 for network in neutron_networks:
95 for template in NetworkTemplate.objects.all():
96 if template.shared_network_name == network["name"]:
97 templates_by_id[network["id"]] = template
98
99 for port in ports_by_id.values():
100 #logger.info("port %s" % str(port))
101 if port["id"] in ports_by_neutron_port:
102 # we already have it
103 #logger.info("already accounted for port %s" % port["id"])
104 continue
105
106 if port["device_owner"] != "compute:nova":
107 # we only want the ports that connect to instances
108 #logger.info("port %s is not a compute port, it is a %s" % (port["id"], port["device_owner"]))
109 continue
110
111 instance = instances_by_instance_uuid.get(port['device_id'], None)
112 if not instance:
113 logger.info("no instance for port %s device_id %s" % (port["id"], port['device_id']))
114 continue
115
116 network = networks_by_id.get(port['network_id'], None)
117 if not network:
118 # maybe it's public-nat or public-dedicated. Search the templates for
119 # the id, then see if the instance's slice has some network that uses
120 # that template
121 template = templates_by_id.get(port['network_id'], None)
122 if template and instance.slice:
123 for candidate_network in instance.slice.networks.all():
124 if candidate_network.template == template:
125 network=candidate_network
126 if not network:
127 logger.info("no network for port %s network %s" % (port["id"], port["network_id"]))
128
129 # we know it's associated with a instance, but we don't know
130 # which network it is part of.
131
132 continue
133
134 if network.template.shared_network_name:
135 # If it's a shared network template, then more than one network
136 # object maps to the neutron network. We have to do a whole bunch
137 # of extra work to find the right one.
138 networks = network.template.network_set.all()
139 network = None
140 for candidate_network in networks:
141 if (candidate_network.owner == instance.slice):
142 logger.info("found network %s" % candidate_network)
143 network = candidate_network
144
145 if not network:
146 logger.info("failed to find the correct network for a shared template for port %s network %s" % (port["id"], port["network_id"]))
147 continue
148
149 if not port["fixed_ips"]:
150 logger.info("port %s has no fixed_ips" % port["id"])
151 continue
152
153 ip=port["fixed_ips"][0]["ip_address"]
154 mac=port["mac_address"]
155 logger.info("creating Port (%s, %s, %s, %s)" % (str(network), str(instance), ip, str(port["id"])))
156
157 ns = Port(network=network,
158 instance=instance,
159 ip=ip,
160 mac=mac,
161 port_id=port["id"])
162
163 try:
164 ns.save()
165 except:
166 logger.log_exc("failed to save port %s" % str(ns))
167 continue
168
169 # For ports that were created by the user, find that ones
Scott Bakeraf599eb2017-03-21 12:43:26 -0700170 # that don't have neutron ports, and create them. These are ports
171 # with a null port_id and a non-null instance_id.
172 ports = Port.objects.all()
173 ports = [x for x in ports if ((not x.port_id) and (x.instance_id))]
174 for port in ports:
Scott Bakerb63ea792016-08-11 10:24:48 -0700175 logger.info("XXX working on port %s" % port)
176 controller = port.instance.node.site_deployment.controller
177 slice = port.instance.slice
178
179 if controller:
Scott Bakeraf599eb2017-03-21 12:43:26 -0700180 cn=[x for x in port.network.controllernetworks.all() if x.controller_id==controller.id]
Scott Bakerb63ea792016-08-11 10:24:48 -0700181 if not cn:
182 logger.log_exc("no controllernetwork for %s" % port)
183 continue
184 cn=cn[0]
185 if cn.lazy_blocked:
186 cn.lazy_blocked=False
187 cn.save()
188 logger.info("deferring port %s because controllerNetwork was lazy-blocked" % port)
189 continue
190 if not cn.net_id:
191 logger.info("deferring port %s because controllerNetwork does not have a port-id yet" % port)
192 continue
193 try:
194 driver = self.get_driver(port)
195
196 args = {"network_id": cn.net_id}
197 neutron_port_name = port.get_parameters().get("neutron_port_name", None)
JianHao0900a272017-02-08 09:18:14 +0000198 neutron_port_ip = port.get_parameters().get("neutron_port_ip", None)
Scott Bakerb63ea792016-08-11 10:24:48 -0700199 if neutron_port_name:
200 args["name"] = neutron_port_name
JianHao0900a272017-02-08 09:18:14 +0000201 if neutron_port_ip:
202 args["fixed_ips"] = [{"ip_address": neutron_port_ip, "subnet_id": cn.subnet_id}]
Scott Bakerb63ea792016-08-11 10:24:48 -0700203
204 neutron_port = driver.shell.neutron.create_port({"port": args})["port"]
205 port.port_id = neutron_port["id"]
206 if neutron_port["fixed_ips"]:
207 port.ip = neutron_port["fixed_ips"][0]["ip_address"]
208 port.mac = neutron_port["mac_address"]
209 port.xos_created = True
210 logger.info("created neutron port %s for %s" % (port.port_id, port))
211 except:
212 logger.log_exc("failed to create neutron port for %s" % port)
213 continue
214 port.save()
215
216 def delete_ports(self):
217 logger.info("sync'ing Ports [delete=True]")
Scott Baker8c542842017-03-22 15:45:44 -0700218 ports = self.fetch_pending(deletion=True)
219 for port in ports:
Scott Bakerb63ea792016-08-11 10:24:48 -0700220 self.delete_record(port)
221
222 def delete_record(self, port):
223 if port.xos_created and port.port_id:
224 logger.info("calling openstack to destroy port %s" % port.port_id)
225 try:
226 driver = self.get_driver(port)
227 driver.shell.neutron.delete_port(port.port_id)
228 except:
229 logger.log_exc("failed to delete port %s from neutron" % port.port_id)
230 return
231
232 logger.info("Purging port %s" % port)
233 port.delete(purge=True)
234