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