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