blob: d9b0f4c3231224c1cda830212e62bf0017079c36 [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
Scott Baker806d4b72019-02-26 19:01:59 -080018from xossynchronizer.steps.syncstep import SyncStep, DeferredException
19from xossynchronizer.modelaccessor import model_accessor
20from xossynchronizer.modelaccessor import ONOSApp, ServiceInstance, ServiceInstanceAttribute
Matteo Scandolo0b986e22018-06-04 14:07:33 -070021
22from xosconfig import Config
23from multistructlog import create_logger
24
25from helpers import Helpers
26
27log = create_logger(Config().get('logging'))
28log.info("config file", file=Config().get_config_file())
29
30class SyncONOSApp(SyncStep):
31 provides = [ONOSApp]
32 observes = [ONOSApp, ServiceInstanceAttribute]
33
34 def get_service_instance_attribute(self, o):
35 # NOTE this method is defined in the core convenience methods for service_instances
36 svc = ServiceInstance.objects.get(id=o.id)
37 return svc.serviceinstanceattribute_dict
38
39 def check_app_dependencies(self, deps):
40 """
41 Check if all the dependencies required by this application are installed
42 :param deps: comma separated list of application names
43 :return: bool
44 """
Scott Baker9ee45e12018-08-21 16:58:39 -070045 if not deps:
46 return True
47 for dep in [x.strip() for x in deps.split(',') if x is not ""]:
Matteo Scandolo0b986e22018-06-04 14:07:33 -070048 try:
49 app = ONOSApp.objects.get(app_id=dep)
50 if not app.backend_code == 1:
51 # backend_code == 1 means that the app has been pushed
52 return False
53 except IndexError, e:
54 return False
55 return True
56
57 def add_config(self, o):
58 log.info("Adding config %s" % o.name, model=o.tologdict())
59 # getting onos url and auth
60 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)
61 onos_basic_auth = HTTPBasicAuth(o.service_instance.leaf_model.owner.leaf_model.rest_username, o.service_instance.leaf_model.owner.leaf_model.rest_password)
62
63 # push configs (if any)
64 url = o.name
65 if url[0] == "/":
66 # strip initial /
67 url = url[1:]
68
69 url = '%s/%s' % (onos_url, url)
70 value = json.loads(o.value)
71 request = requests.post(url, json=value, auth=onos_basic_auth)
72
73 if request.status_code != 200:
74 log.error("Request failed", response=request.text)
75 raise Exception("Failed to add config %s in ONOS: %s" % (url, request.text))
76
77 def activate_app(self, o, onos_url, onos_basic_auth):
78 log.info("Activating app %s" % o.app_id)
79 url = '%s/onos/v1/applications/%s/active' % (onos_url, o.app_id)
80 request = requests.post(url, auth=onos_basic_auth)
81
82 if request.status_code != 200:
83 log.error("Request failed", response=request.text)
84 raise Exception("Failed to add application %s to ONOS: %s" % (url, request.text))
85
86 url = '%s/onos/v1/applications/%s' % (onos_url, o.app_id)
87 request = requests.get(url, auth=onos_basic_auth)
88
89 if request.status_code != 200:
90 log.error("Request failed", response=request.text)
91 raise Exception("Failed to read application %s from ONOS: %s" % (url, request.text))
92 else:
93 o.version = request.json()["version"]
94
95 def check_app_installed(self, o, onos_url, onos_basic_auth):
Matteo Scandolo3b672572018-06-25 14:28:36 -070096 log.debug("Checking if app is installed", app=o.app_id)
Matteo Scandolo0b986e22018-06-04 14:07:33 -070097 url = '%s/onos/v1/applications/%s' % (onos_url, o.app_id)
98 request = requests.get(url, auth=onos_basic_auth)
99
100 if request.status_code == 200:
101 if "version" in request.json() and o.version == request.json()["version"]:
Matteo Scandolo3b672572018-06-25 14:28:36 -0700102 log.debug("App is installed", app=o.app_id)
Matteo Scandolo0b986e22018-06-04 14:07:33 -0700103 return True
104 else:
105 # uninstall the application
106 self.uninstall_app(o, onos_url, onos_basic_auth)
107 return False
108 if request.status_code == 404:
109 # app is not installed at all
110 return False
111 else:
112 log.error("Request failed", response=request.text)
Matteo Scandolo3b672572018-06-25 14:28:36 -0700113 raise Exception("Failed to read application %s from ONOS aaa: %s" % (url, request.text))
Matteo Scandolo0b986e22018-06-04 14:07:33 -0700114
115 def install_app(self, o, onos_url, onos_basic_auth):
Matteo Scandolo3b672572018-06-25 14:28:36 -0700116 log.info("Installing app from url %s" % o.url, app=o.app_id, version=o.version)
Matteo Scandolo0b986e22018-06-04 14:07:33 -0700117
Matteo Scandolo3b672572018-06-25 14:28:36 -0700118 # check is the already installed app is the correct version
119 is_installed = self.check_app_installed(o, onos_url, onos_basic_auth)
Matteo Scandolo0b986e22018-06-04 14:07:33 -0700120
121 if is_installed:
122 # if the app is already installed we don't need to do anything
Matteo Scandolo3b672572018-06-25 14:28:36 -0700123 log.info("App is installed, skipping install", app=o.app_id)
Matteo Scandolo0b986e22018-06-04 14:07:33 -0700124 return
125
Matteo Scandolo0b986e22018-06-04 14:07:33 -0700126 data = {
127 'activate': True,
128 'url': o.url
129 }
130 url = '%s/onos/v1/applications' % onos_url
131 request = requests.post(url, json=data, auth=onos_basic_auth)
132
Matteo Scandolo3b672572018-06-25 14:28:36 -0700133 if request.status_code == 409:
134 log.info("App was already installed", app=o.app_id, test=request.text)
135 return
136
Matteo Scandolo0b986e22018-06-04 14:07:33 -0700137 if request.status_code != 200:
138 log.error("Request failed", response=request.text)
139 raise Exception("Failed to add application %s to ONOS: %s" % (url, request.text))
140
Matteo Scandolo3b672572018-06-25 14:28:36 -0700141 log.debug("App from url %s installed" % o.url, app=o.app_id, version=o.version)
Matteo Scandolo0b986e22018-06-04 14:07:33 -0700142
143 url = '%s/onos/v1/applications/%s' % (onos_url, o.app_id)
144 request = requests.get(url, auth=onos_basic_auth)
145
146 if request.status_code != 200:
147 log.error("Request failed", response=request.text)
Matteo Scandolo3b672572018-06-25 14:28:36 -0700148 raise Exception("Failed to read application %s from ONOS: %s while checking correct version" % (url, request.text))
Matteo Scandolo0b986e22018-06-04 14:07:33 -0700149 else:
150 if o.version != request.json()["version"]:
151 raise Exception("The version of %s you installed (%s) is not the same you requested (%s)" % (o.app_id, request.json()["version"], o.version))
152
153 def sync_record(self, o):
154 log.info("Sync'ing", model=o.tologdict())
155 if hasattr(o, 'service_instance'):
156 # this is a ServiceInstanceAttribute model just push the config
157 if 'ONOSApp' in o.service_instance.leaf_model.class_names:
158 return self.add_config(o)
159 return # if it's not an ONOSApp do nothing
160
161 if not self.check_app_dependencies(o.dependencies):
162 raise DeferredException('Deferring installation of ONOSApp with id %s as dependencies are not met' % o.id)
163
164 # getting onos url and auth
165 onos_url = "%s:%s" % (Helpers.format_url(o.owner.leaf_model.rest_hostname), o.owner.leaf_model.rest_port)
166 onos_basic_auth = HTTPBasicAuth(o.owner.leaf_model.rest_username, o.owner.leaf_model.rest_password)
167
168 # activate app (bundled in onos)
169 if not o.url or o.url is None:
170 self.activate_app(o, onos_url, onos_basic_auth)
171 # install an app from a remote source
172 if o.url and o.url is not None:
173 self.install_app(o, onos_url, onos_basic_auth)
174
175 def delete_config(self, o):
176 log.info("Deleting config %s" % o.name)
177 # getting onos url and auth
178 onos_app = o.service_instance.leaf_model
179 onos_url = "%s:%s" % (Helpers.format_url(onos_app.owner.leaf_model.rest_hostname), onos_app.owner.leaf_model.rest_port)
180 onos_basic_auth = HTTPBasicAuth(onos_app.owner.leaf_model.rest_username, onos_app.owner.leaf_model.rest_password)
181
182 url = o.name
183 if url[0] == "/":
184 # strip initial /
185 url = url[1:]
186
187 url = '%s/%s' % (onos_url, url)
188 request = requests.delete(url, auth=onos_basic_auth)
189
190 if request.status_code != 204:
191 log.error("Request failed", response=request.text)
192 raise Exception("Failed to remove config %s from ONOS: %s" % (url, request.text))
193
194 def uninstall_app(self,o, onos_url, onos_basic_auth):
195 log.info("Uninstalling app %s" % o.app_id)
196 url = '%s/onos/v1/applications/%s' % (onos_url, o.app_id)
197
198 request = requests.delete(url, auth=onos_basic_auth)
199
200 if request.status_code != 204:
201 log.error("Request failed", response=request.text)
202 raise Exception("Failed to delete application %s from ONOS: %s" % (url, request.text))
203
204 def deactivate_app(self, o, onos_url, onos_basic_auth):
205 log.info("Deactivating app %s" % o.app_id)
206 url = '%s/onos/v1/applications/%s/active' % (onos_url, o.app_id)
207
208 request = requests.delete(url, auth=onos_basic_auth)
209
210 if request.status_code != 204:
211 log.error("Request failed", response=request.text)
212 raise Exception("Failed to deactivate application %s from ONOS: %s" % (url, request.text))
213
214 def delete_record(self, o):
215
216 if hasattr(o, 'service_instance'):
217 # this is a ServiceInstanceAttribute model
218 if 'ONOSApp' in o.service_instance.leaf_model.class_names:
219 return self.delete_config(o)
220 return # if it's not related to an ONOSApp do nothing
221
222 # NOTE if it is an ONOSApp we don't care about the ServiceInstanceAttribute
223 # as the reaper will delete it
224
225 # getting onos url and auth
226 onos_url = "%s:%s" % (Helpers.format_url(o.owner.leaf_model.rest_hostname), o.owner.leaf_model.rest_port)
227 onos_basic_auth = HTTPBasicAuth(o.owner.leaf_model.rest_username, o.owner.leaf_model.rest_password)
228
229 # deactivate an app (bundled in onos)
230 if not o.url or o.url is None:
231 self.deactivate_app(o, onos_url, onos_basic_auth)
232 # uninstall an app from a remote source, only if it has been activated before
233 if o.url and o.url is not None:
234 self.uninstall_app(o, onos_url, onos_basic_auth)