blob: 82e24e3c9cad30a243ba217df9bc96483a98b0ad [file] [log] [blame]
Matteo Scandolo0b986e22018-06-04 14:07:33 -07001# Copyright 2017-present Open Networking Foundation
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15import json
16import requests
17from requests.auth import HTTPBasicAuth
18from synchronizers.new_base.syncstep import SyncStep, DeferredException, model_accessor
19from synchronizers.new_base.modelaccessor import ONOSApp, ServiceInstance, ServiceInstanceAttribute
20
21from xosconfig import Config
22from multistructlog import create_logger
23
24from helpers import Helpers
25
26log = create_logger(Config().get('logging'))
27log.info("config file", file=Config().get_config_file())
28
29class SyncONOSApp(SyncStep):
30 provides = [ONOSApp]
31 observes = [ONOSApp, ServiceInstanceAttribute]
32
33 def get_service_instance_attribute(self, o):
34 # NOTE this method is defined in the core convenience methods for service_instances
35 svc = ServiceInstance.objects.get(id=o.id)
36 return svc.serviceinstanceattribute_dict
37
38 def check_app_dependencies(self, deps):
39 """
40 Check if all the dependencies required by this application are installed
41 :param deps: comma separated list of application names
42 :return: bool
43 """
Scott Baker9ee45e12018-08-21 16:58:39 -070044 if not deps:
45 return True
46 for dep in [x.strip() for x in deps.split(',') if x is not ""]:
Matteo Scandolo0b986e22018-06-04 14:07:33 -070047 try:
48 app = ONOSApp.objects.get(app_id=dep)
49 if not app.backend_code == 1:
50 # backend_code == 1 means that the app has been pushed
51 return False
52 except IndexError, e:
53 return False
54 return True
55
56 def add_config(self, o):
57 log.info("Adding config %s" % o.name, model=o.tologdict())
58 # getting onos url and auth
59 onos_url = "%s:%s" % (Helpers.format_url(o.service_instance.leaf_model.owner.leaf_model.rest_hostname), o.service_instance.leaf_model.owner.leaf_model.rest_port)
60 onos_basic_auth = HTTPBasicAuth(o.service_instance.leaf_model.owner.leaf_model.rest_username, o.service_instance.leaf_model.owner.leaf_model.rest_password)
61
62 # push configs (if any)
63 url = o.name
64 if url[0] == "/":
65 # strip initial /
66 url = url[1:]
67
68 url = '%s/%s' % (onos_url, url)
69 value = json.loads(o.value)
70 request = requests.post(url, json=value, auth=onos_basic_auth)
71
72 if request.status_code != 200:
73 log.error("Request failed", response=request.text)
74 raise Exception("Failed to add config %s in ONOS: %s" % (url, request.text))
75
76 def activate_app(self, o, onos_url, onos_basic_auth):
77 log.info("Activating app %s" % o.app_id)
78 url = '%s/onos/v1/applications/%s/active' % (onos_url, o.app_id)
79 request = requests.post(url, auth=onos_basic_auth)
80
81 if request.status_code != 200:
82 log.error("Request failed", response=request.text)
83 raise Exception("Failed to add application %s to ONOS: %s" % (url, request.text))
84
85 url = '%s/onos/v1/applications/%s' % (onos_url, o.app_id)
86 request = requests.get(url, auth=onos_basic_auth)
87
88 if request.status_code != 200:
89 log.error("Request failed", response=request.text)
90 raise Exception("Failed to read application %s from ONOS: %s" % (url, request.text))
91 else:
92 o.version = request.json()["version"]
93
94 def check_app_installed(self, o, onos_url, onos_basic_auth):
Matteo Scandolo3b672572018-06-25 14:28:36 -070095 log.debug("Checking if app is installed", app=o.app_id)
Matteo Scandolo0b986e22018-06-04 14:07:33 -070096 url = '%s/onos/v1/applications/%s' % (onos_url, o.app_id)
97 request = requests.get(url, auth=onos_basic_auth)
98
99 if request.status_code == 200:
100 if "version" in request.json() and o.version == request.json()["version"]:
Matteo Scandolo3b672572018-06-25 14:28:36 -0700101 log.debug("App is installed", app=o.app_id)
Matteo Scandolo0b986e22018-06-04 14:07:33 -0700102 return True
103 else:
104 # uninstall the application
105 self.uninstall_app(o, onos_url, onos_basic_auth)
106 return False
107 if request.status_code == 404:
108 # app is not installed at all
109 return False
110 else:
111 log.error("Request failed", response=request.text)
Matteo Scandolo3b672572018-06-25 14:28:36 -0700112 raise Exception("Failed to read application %s from ONOS aaa: %s" % (url, request.text))
Matteo Scandolo0b986e22018-06-04 14:07:33 -0700113
114 def install_app(self, o, onos_url, onos_basic_auth):
Matteo Scandolo3b672572018-06-25 14:28:36 -0700115 log.info("Installing app from url %s" % o.url, app=o.app_id, version=o.version)
Matteo Scandolo0b986e22018-06-04 14:07:33 -0700116
Matteo Scandolo3b672572018-06-25 14:28:36 -0700117 # check is the already installed app is the correct version
118 is_installed = self.check_app_installed(o, onos_url, onos_basic_auth)
Matteo Scandolo0b986e22018-06-04 14:07:33 -0700119
120 if is_installed:
121 # if the app is already installed we don't need to do anything
Matteo Scandolo3b672572018-06-25 14:28:36 -0700122 log.info("App is installed, skipping install", app=o.app_id)
Matteo Scandolo0b986e22018-06-04 14:07:33 -0700123 return
124
Matteo Scandolo0b986e22018-06-04 14:07:33 -0700125 data = {
126 'activate': True,
127 'url': o.url
128 }
129 url = '%s/onos/v1/applications' % onos_url
130 request = requests.post(url, json=data, auth=onos_basic_auth)
131
Matteo Scandolo3b672572018-06-25 14:28:36 -0700132 if request.status_code == 409:
133 log.info("App was already installed", app=o.app_id, test=request.text)
134 return
135
Matteo Scandolo0b986e22018-06-04 14:07:33 -0700136 if request.status_code != 200:
137 log.error("Request failed", response=request.text)
138 raise Exception("Failed to add application %s to ONOS: %s" % (url, request.text))
139
Matteo Scandolo3b672572018-06-25 14:28:36 -0700140 log.debug("App from url %s installed" % o.url, app=o.app_id, version=o.version)
Matteo Scandolo0b986e22018-06-04 14:07:33 -0700141
142 url = '%s/onos/v1/applications/%s' % (onos_url, o.app_id)
143 request = requests.get(url, auth=onos_basic_auth)
144
145 if request.status_code != 200:
146 log.error("Request failed", response=request.text)
Matteo Scandolo3b672572018-06-25 14:28:36 -0700147 raise Exception("Failed to read application %s from ONOS: %s while checking correct version" % (url, request.text))
Matteo Scandolo0b986e22018-06-04 14:07:33 -0700148 else:
149 if o.version != request.json()["version"]:
150 raise Exception("The version of %s you installed (%s) is not the same you requested (%s)" % (o.app_id, request.json()["version"], o.version))
151
152 def sync_record(self, o):
153 log.info("Sync'ing", model=o.tologdict())
154 if hasattr(o, 'service_instance'):
155 # this is a ServiceInstanceAttribute model just push the config
156 if 'ONOSApp' in o.service_instance.leaf_model.class_names:
157 return self.add_config(o)
158 return # if it's not an ONOSApp do nothing
159
160 if not self.check_app_dependencies(o.dependencies):
161 raise DeferredException('Deferring installation of ONOSApp with id %s as dependencies are not met' % o.id)
162
163 # getting onos url and auth
164 onos_url = "%s:%s" % (Helpers.format_url(o.owner.leaf_model.rest_hostname), o.owner.leaf_model.rest_port)
165 onos_basic_auth = HTTPBasicAuth(o.owner.leaf_model.rest_username, o.owner.leaf_model.rest_password)
166
167 # activate app (bundled in onos)
168 if not o.url or o.url is None:
169 self.activate_app(o, onos_url, onos_basic_auth)
170 # install an app from a remote source
171 if o.url and o.url is not None:
172 self.install_app(o, onos_url, onos_basic_auth)
173
174 def delete_config(self, o):
175 log.info("Deleting config %s" % o.name)
176 # getting onos url and auth
177 onos_app = o.service_instance.leaf_model
178 onos_url = "%s:%s" % (Helpers.format_url(onos_app.owner.leaf_model.rest_hostname), onos_app.owner.leaf_model.rest_port)
179 onos_basic_auth = HTTPBasicAuth(onos_app.owner.leaf_model.rest_username, onos_app.owner.leaf_model.rest_password)
180
181 url = o.name
182 if url[0] == "/":
183 # strip initial /
184 url = url[1:]
185
186 url = '%s/%s' % (onos_url, url)
187 request = requests.delete(url, auth=onos_basic_auth)
188
189 if request.status_code != 204:
190 log.error("Request failed", response=request.text)
191 raise Exception("Failed to remove config %s from ONOS: %s" % (url, request.text))
192
193 def uninstall_app(self,o, onos_url, onos_basic_auth):
194 log.info("Uninstalling app %s" % o.app_id)
195 url = '%s/onos/v1/applications/%s' % (onos_url, o.app_id)
196
197 request = requests.delete(url, auth=onos_basic_auth)
198
199 if request.status_code != 204:
200 log.error("Request failed", response=request.text)
201 raise Exception("Failed to delete application %s from ONOS: %s" % (url, request.text))
202
203 def deactivate_app(self, o, onos_url, onos_basic_auth):
204 log.info("Deactivating app %s" % o.app_id)
205 url = '%s/onos/v1/applications/%s/active' % (onos_url, o.app_id)
206
207 request = requests.delete(url, auth=onos_basic_auth)
208
209 if request.status_code != 204:
210 log.error("Request failed", response=request.text)
211 raise Exception("Failed to deactivate application %s from ONOS: %s" % (url, request.text))
212
213 def delete_record(self, o):
214
215 if hasattr(o, 'service_instance'):
216 # this is a ServiceInstanceAttribute model
217 if 'ONOSApp' in o.service_instance.leaf_model.class_names:
218 return self.delete_config(o)
219 return # if it's not related to an ONOSApp do nothing
220
221 # NOTE if it is an ONOSApp we don't care about the ServiceInstanceAttribute
222 # as the reaper will delete it
223
224 # getting onos url and auth
225 onos_url = "%s:%s" % (Helpers.format_url(o.owner.leaf_model.rest_hostname), o.owner.leaf_model.rest_port)
226 onos_basic_auth = HTTPBasicAuth(o.owner.leaf_model.rest_username, o.owner.leaf_model.rest_password)
227
228 # deactivate an app (bundled in onos)
229 if not o.url or o.url is None:
230 self.deactivate_app(o, onos_url, onos_basic_auth)
231 # uninstall an app from a remote source, only if it has been activated before
232 if o.url and o.url is not None:
233 self.uninstall_app(o, onos_url, onos_basic_auth)