blob: e4d4b4f9f5a3f24e8da4249c2817c4e5f9e531c2 [file] [log] [blame]
Andy Bavier7b321c52017-08-30 15:33:59 -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
17import os
18import requests
19import socket
20import sys
21import base64
22from synchronizers.vtn.vtnnetport import VTNNetwork, VTNPort
23from synchronizers.new_base.syncstep import SyncStep
24from synchronizers.new_base.modelaccessor import *
25from xos.logger import Logger, logging
26from requests.auth import HTTPBasicAuth
27
28logger = Logger(level=logging.INFO)
29
30# XXX should save and load this
31glo_saved_networks = {}
32glo_saved_ports = {}
33
34class SyncVTNService(SyncStep):
35 provides=[Service]
36 observes=Service
37 requested_interval=0
38
39 def __init__(self, **args):
40 SyncStep.__init__(self, **args)
41
42 def get_vtn_onos_app(self, vtn_service):
43 links = vtn_service.subscribed_links.all()
44 for link in links:
45 # We're looking for an ONOS App. It's the only ServiceInstance that VTN can be implemented on.
46 if link.provider_service_instance.leaf_model_name != "ONOSApp":
47 continue
48
49 # TODO: Rather than checking model name, check for the right interface
50 # NOTE: Deferred until new Tosca engine is in place.
51
52 #if not link.provider_service_interface:
53 # logger.warning("Link %s does not have a provider_service_interface. Skipping" % link)
54 # continue
55 #
56 #if link.provider_service_interface.interface_type.name != "onos_app_interface":
57 # logger.warning("Link %s provider_service_interface type is not equal to onos_app_interface" % link)
58 # continue
59
60 # cast from ServiceInstance to ONOSApp
61 app = link.provider_service_instance.leaf_model
62 return app
63
64 raise Exception("No ServiceInstanceLink from VTN Service to VTN ONOS App")
65
66 def get_vtn_endpoint(self, vtn_service):
67 """ Get connection info for the ONOS that is hosting the VTN ONOS App.
68
69 returns (hostname, port, auth)
70 """
71 app = self.get_vtn_onos_app(vtn_service)
72 # cast from Service to ONOSService
73 onos = app.owner.leaf_model
74 if not (onos.rest_hostname):
75 raise Exception("onos.rest_hostname is not set")
76 if not (onos.rest_port):
77 raise Exception("onos.rest_port is not set")
78 if not (onos.rest_password):
79 raise Exception("onos.rest_password is not set")
80 if not (onos.rest_username):
81 raise Exception("onos.rest_username is not set")
82 auth = HTTPBasicAuth(onos.rest_username, onos.rest_password)
83 return (onos.rest_hostname, onos.rest_port, auth)
84
85 def get_method(self, auth, url, id):
86 url_with_id = "%s/%s" % (url, id)
87 r = requests.get(url_with_id, auth=auth)
88 if (r.status_code==200):
89 method="PUT"
90 url = url_with_id
91 req_func = requests.put
92 exists=True
93 else:
94 method="POST"
95 req_func = requests.post
96 exists=False
97 return (exists, url, method, req_func)
98
99 def sync_service_networks(self, vtn_service):
100 (onos_hostname, onos_port, onos_auth) = self.get_vtn_endpoint(vtn_service)
101
102 valid_ids = []
103 for network in Network.objects.all():
104 network = VTNNetwork(network)
105
106 if not network.id:
107 continue
108
109 if (network.type=="PRIVATE") and (not network.providerNetworks):
110 logger.info("Skipping network %s because it has no relevant state" % network.id)
111 continue
112
113 valid_ids.append(network.id)
114
115 if (glo_saved_networks.get(network.id, None) != network.to_dict()):
116 (exists, url, method, req_func) = self.get_method(onos_auth, "http://%s:%d/onos/cordvtn/serviceNetworks" % (onos_hostname, onos_port), network.id)
117
118 logger.info("%sing VTN API for network %s" % (method, network.id))
119
120 logger.info("URL: %s" % url)
121
122 # clean the providerNetworks list
123 providerNetworks = [{"id": x["id"], "bidirectional": x["bidirectional"]} for x in network.providerNetworks]
124
125 data = {"ServiceNetwork": {"id": network.id,
126 "type": network.type,
127 "providerNetworks": providerNetworks} }
128 logger.info("DATA: %s" % str(data))
129
130 r=req_func(url, json=data, auth=onos_auth )
131 if (r.status_code in [200,201]):
132 glo_saved_networks[network.id] = network.to_dict()
133 else:
134 logger.error("Received error from vtn service (%d)" % r.status_code)
135
136
137 for network_id in glo_saved_networks.keys():
138 if network_id not in valid_ids:
139 logger.info("DELETEing VTN API for network %s" % network_id)
140
141 url = "http://%s:%d/onos/cordvtn/serviceNetworks/%s" % (onos_hostname, onos_port, network_id)
142 logger.info("URL: %s" % url)
143
144 r = requests.delete(url, auth=onos_auth )
145 if (r.status_code in [200,204]):
146 del glo_saved_networks[network_id]
147 else:
148 logger.error("Received error from vtn service (%d)" % r.status_code)
149
150 def sync_service_ports(self, vtn_service):
151 (onos_hostname, onos_port, onos_auth) = self.get_vtn_endpoint(vtn_service)
152
153 valid_ids = []
154 for port in Port.objects.all():
155 port = VTNPort(port)
156
157 if not port.id:
158 continue
159
160 if (not port.vlan_id) and (not port.floating_address_pairs):
161 logger.info("Skipping port %s because it has no relevant state" % port.id)
162 continue
163
164 valid_ids.append(port.id)
165
166 if (glo_saved_ports.get(port.id, None) != port.to_dict()):
167 (exists, url, method, req_func) = self.get_method(onos_auth, "http://%s:%d/onos/cordvtn/servicePorts" % (onos_hostname, onos_port), port.id)
168
169 logger.info("%sing VTN API for port %s" % (method, port.id))
170
171 logger.info("URL: %s" % url)
172
173 data = {"ServicePort": {"id": port.id,
174 "vlan_id": port.vlan_id,
175 "floating_address_pairs": port.floating_address_pairs} }
176 logger.info("DATA: %s" % str(data))
177
178 r=req_func(url, json=data, auth=onos_auth )
179 if (r.status_code in [200,201]):
180 glo_saved_ports[port.id] = port.to_dict()
181 else:
182 logger.error("Received error from vtn service (%d)" % r.status_code)
183
184 for port_id in glo_saved_ports.keys():
185 if port_id not in valid_ids:
186 logger.info("DELETEing VTN API for port %s" % port_id)
187
188 url = "http://%s:%d/onos/cordvtn/servicePorts/%s" % (onos_hostname, onos_port, port_id)
189 logger.info("URL: %s" % url)
190
191 r = requests.delete(url, auth=onos_auth )
192 if (r.status_code in [200,204]):
193 del glo_saved_ports[port_id]
194 else:
195 logger.error("Received error from vtn service (%d)" % r.status_code)
196
197 def call(self, **args):
198 global glo_saved_networks
199 global glo_saved_ports
200
201 vtn_service = VTNService.objects.all()
202 if not vtn_service:
203 raise Exception("No VTN Service")
204
205 vtn_service = vtn_service[0]
206
207 # TODO: We should check get_vtn_onos_app() and make sure that it has been synced, and that any necessary
208 # attributes (netcfg, etc) is filled out.
209
210 if (vtn_service.resync):
211 # If the VTN app requested a full resync, clear our saved network
212 # so we will resync everything, then reset the 'resync' flag
213 glo_saved_networks = {}
214 glo_saved_ports = {}
215
216 vtn_service.resync = False
217 vtn_service.save()
218
219 if vtn_service.vtnAPIVersion>=2:
220 # version 2 means use new API
221 logger.info("Using New API")
222 self.sync_service_networks(vtn_service)
223 self.sync_service_ports(vtn_service)
224 else:
225 raise Exception("VTN API Version 1 is no longer supported by VTN Synchronizer")
226
227