blob: 210f8cea4c8c1df655b4ffbb54f845135975f256 [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
Scott Baker7848fa52019-04-01 17:54:36 -070030
Matteo Scandolo0b986e22018-06-04 14:07:33 -070031class SyncONOSApp(SyncStep):
32 provides = [ONOSApp]
33 observes = [ONOSApp, ServiceInstanceAttribute]
34
35 def get_service_instance_attribute(self, o):
36 # NOTE this method is defined in the core convenience methods for service_instances
37 svc = ServiceInstance.objects.get(id=o.id)
38 return svc.serviceinstanceattribute_dict
39
40 def check_app_dependencies(self, deps):
41 """
42 Check if all the dependencies required by this application are installed
43 :param deps: comma separated list of application names
44 :return: bool
45 """
Scott Baker9ee45e12018-08-21 16:58:39 -070046 if not deps:
47 return True
Scott Baker7848fa52019-04-01 17:54:36 -070048 for dep in [x.strip() for x in deps.split(',') if x != ""]:
Matteo Scandolo0b986e22018-06-04 14:07:33 -070049 try:
50 app = ONOSApp.objects.get(app_id=dep)
51 if not app.backend_code == 1:
52 # backend_code == 1 means that the app has been pushed
53 return False
Scott Baker7848fa52019-04-01 17:54:36 -070054 except IndexError:
Matteo Scandolo0b986e22018-06-04 14:07:33 -070055 return False
56 return True
57
58 def add_config(self, o):
59 log.info("Adding config %s" % o.name, model=o.tologdict())
60 # getting onos url and auth
Scott Baker7848fa52019-04-01 17:54:36 -070061 onos_url = "%s:%s" % (Helpers.format_url(
62 o.service_instance.leaf_model.owner.leaf_model.rest_hostname),
63 o.service_instance.leaf_model.owner.leaf_model.rest_port)
64 onos_basic_auth = HTTPBasicAuth(
65 o.service_instance.leaf_model.owner.leaf_model.rest_username,
66 o.service_instance.leaf_model.owner.leaf_model.rest_password)
Matteo Scandolo0b986e22018-06-04 14:07:33 -070067
68 # push configs (if any)
69 url = o.name
70 if url[0] == "/":
71 # strip initial /
72 url = url[1:]
73
74 url = '%s/%s' % (onos_url, url)
75 value = json.loads(o.value)
76 request = requests.post(url, json=value, auth=onos_basic_auth)
77
78 if request.status_code != 200:
79 log.error("Request failed", response=request.text)
80 raise Exception("Failed to add config %s in ONOS: %s" % (url, request.text))
81
82 def activate_app(self, o, onos_url, onos_basic_auth):
83 log.info("Activating app %s" % o.app_id)
84 url = '%s/onos/v1/applications/%s/active' % (onos_url, o.app_id)
85 request = requests.post(url, auth=onos_basic_auth)
86
87 if request.status_code != 200:
88 log.error("Request failed", response=request.text)
89 raise Exception("Failed to add application %s to ONOS: %s" % (url, request.text))
90
91 url = '%s/onos/v1/applications/%s' % (onos_url, o.app_id)
92 request = requests.get(url, auth=onos_basic_auth)
93
94 if request.status_code != 200:
95 log.error("Request failed", response=request.text)
96 raise Exception("Failed to read application %s from ONOS: %s" % (url, request.text))
97 else:
98 o.version = request.json()["version"]
99
100 def check_app_installed(self, o, onos_url, onos_basic_auth):
Matteo Scandolo3b672572018-06-25 14:28:36 -0700101 log.debug("Checking if app is installed", app=o.app_id)
Matteo Scandolo0b986e22018-06-04 14:07:33 -0700102 url = '%s/onos/v1/applications/%s' % (onos_url, o.app_id)
103 request = requests.get(url, auth=onos_basic_auth)
104
105 if request.status_code == 200:
106 if "version" in request.json() and o.version == request.json()["version"]:
Matteo Scandolo3b672572018-06-25 14:28:36 -0700107 log.debug("App is installed", app=o.app_id)
Matteo Scandolo0b986e22018-06-04 14:07:33 -0700108 return True
109 else:
110 # uninstall the application
111 self.uninstall_app(o, onos_url, onos_basic_auth)
112 return False
113 if request.status_code == 404:
114 # app is not installed at all
115 return False
116 else:
117 log.error("Request failed", response=request.text)
Matteo Scandolo3b672572018-06-25 14:28:36 -0700118 raise Exception("Failed to read application %s from ONOS aaa: %s" % (url, request.text))
Matteo Scandolo0b986e22018-06-04 14:07:33 -0700119
120 def install_app(self, o, onos_url, onos_basic_auth):
Matteo Scandolo3b672572018-06-25 14:28:36 -0700121 log.info("Installing app from url %s" % o.url, app=o.app_id, version=o.version)
Matteo Scandolo0b986e22018-06-04 14:07:33 -0700122
Matteo Scandolo3b672572018-06-25 14:28:36 -0700123 # check is the already installed app is the correct version
124 is_installed = self.check_app_installed(o, onos_url, onos_basic_auth)
Matteo Scandolo0b986e22018-06-04 14:07:33 -0700125
126 if is_installed:
127 # if the app is already installed we don't need to do anything
Matteo Scandolo3b672572018-06-25 14:28:36 -0700128 log.info("App is installed, skipping install", app=o.app_id)
Matteo Scandolo0b986e22018-06-04 14:07:33 -0700129 return
130
Matteo Scandolo0b986e22018-06-04 14:07:33 -0700131 data = {
132 'activate': True,
133 'url': o.url
134 }
135 url = '%s/onos/v1/applications' % onos_url
136 request = requests.post(url, json=data, auth=onos_basic_auth)
137
Matteo Scandolo3b672572018-06-25 14:28:36 -0700138 if request.status_code == 409:
139 log.info("App was already installed", app=o.app_id, test=request.text)
140 return
141
Matteo Scandolo0b986e22018-06-04 14:07:33 -0700142 if request.status_code != 200:
143 log.error("Request failed", response=request.text)
144 raise Exception("Failed to add application %s to ONOS: %s" % (url, request.text))
145
Matteo Scandolo3b672572018-06-25 14:28:36 -0700146 log.debug("App from url %s installed" % o.url, app=o.app_id, version=o.version)
Matteo Scandolo0b986e22018-06-04 14:07:33 -0700147
148 url = '%s/onos/v1/applications/%s' % (onos_url, o.app_id)
149 request = requests.get(url, auth=onos_basic_auth)
150
151 if request.status_code != 200:
152 log.error("Request failed", response=request.text)
Scott Baker7848fa52019-04-01 17:54:36 -0700153 raise Exception(
154 "Failed to read application %s from ONOS: %s while checking correct version" %
155 (url, request.text))
Matteo Scandolo0b986e22018-06-04 14:07:33 -0700156 else:
157 if o.version != request.json()["version"]:
Scott Baker7848fa52019-04-01 17:54:36 -0700158 raise Exception(
159 "The version of %s you installed (%s) is not the same you requested (%s)" %
160 (o.app_id, request.json()["version"], o.version))
Matteo Scandolo0b986e22018-06-04 14:07:33 -0700161
162 def sync_record(self, o):
163 log.info("Sync'ing", model=o.tologdict())
164 if hasattr(o, 'service_instance'):
165 # this is a ServiceInstanceAttribute model just push the config
166 if 'ONOSApp' in o.service_instance.leaf_model.class_names:
167 return self.add_config(o)
Scott Baker7848fa52019-04-01 17:54:36 -0700168 return # if it's not an ONOSApp do nothing
Matteo Scandolo0b986e22018-06-04 14:07:33 -0700169
170 if not self.check_app_dependencies(o.dependencies):
171 raise DeferredException('Deferring installation of ONOSApp with id %s as dependencies are not met' % o.id)
172
173 # getting onos url and auth
174 onos_url = "%s:%s" % (Helpers.format_url(o.owner.leaf_model.rest_hostname), o.owner.leaf_model.rest_port)
175 onos_basic_auth = HTTPBasicAuth(o.owner.leaf_model.rest_username, o.owner.leaf_model.rest_password)
176
177 # activate app (bundled in onos)
178 if not o.url or o.url is None:
179 self.activate_app(o, onos_url, onos_basic_auth)
180 # install an app from a remote source
181 if o.url and o.url is not None:
182 self.install_app(o, onos_url, onos_basic_auth)
183
184 def delete_config(self, o):
185 log.info("Deleting config %s" % o.name)
186 # getting onos url and auth
187 onos_app = o.service_instance.leaf_model
Scott Baker7848fa52019-04-01 17:54:36 -0700188 onos_url = "%s:%s" % (Helpers.format_url(
189 onos_app.owner.leaf_model.rest_hostname),
190 onos_app.owner.leaf_model.rest_port)
191 onos_basic_auth = HTTPBasicAuth(
192 onos_app.owner.leaf_model.rest_username,
193 onos_app.owner.leaf_model.rest_password)
Matteo Scandolo0b986e22018-06-04 14:07:33 -0700194
195 url = o.name
196 if url[0] == "/":
197 # strip initial /
198 url = url[1:]
199
200 url = '%s/%s' % (onos_url, url)
201 request = requests.delete(url, auth=onos_basic_auth)
202
203 if request.status_code != 204:
204 log.error("Request failed", response=request.text)
205 raise Exception("Failed to remove config %s from ONOS: %s" % (url, request.text))
206
Scott Baker7848fa52019-04-01 17:54:36 -0700207 def uninstall_app(self, o, onos_url, onos_basic_auth):
Matteo Scandolo0b986e22018-06-04 14:07:33 -0700208 log.info("Uninstalling app %s" % o.app_id)
209 url = '%s/onos/v1/applications/%s' % (onos_url, o.app_id)
210
211 request = requests.delete(url, auth=onos_basic_auth)
212
213 if request.status_code != 204:
214 log.error("Request failed", response=request.text)
215 raise Exception("Failed to delete application %s from ONOS: %s" % (url, request.text))
216
217 def deactivate_app(self, o, onos_url, onos_basic_auth):
218 log.info("Deactivating app %s" % o.app_id)
219 url = '%s/onos/v1/applications/%s/active' % (onos_url, o.app_id)
220
221 request = requests.delete(url, auth=onos_basic_auth)
222
223 if request.status_code != 204:
224 log.error("Request failed", response=request.text)
225 raise Exception("Failed to deactivate application %s from ONOS: %s" % (url, request.text))
226
227 def delete_record(self, o):
228
229 if hasattr(o, 'service_instance'):
230 # this is a ServiceInstanceAttribute model
231 if 'ONOSApp' in o.service_instance.leaf_model.class_names:
232 return self.delete_config(o)
Scott Baker7848fa52019-04-01 17:54:36 -0700233 return # if it's not related to an ONOSApp do nothing
Matteo Scandolo0b986e22018-06-04 14:07:33 -0700234
235 # NOTE if it is an ONOSApp we don't care about the ServiceInstanceAttribute
236 # as the reaper will delete it
237
238 # getting onos url and auth
239 onos_url = "%s:%s" % (Helpers.format_url(o.owner.leaf_model.rest_hostname), o.owner.leaf_model.rest_port)
240 onos_basic_auth = HTTPBasicAuth(o.owner.leaf_model.rest_username, o.owner.leaf_model.rest_password)
241
242 # deactivate an app (bundled in onos)
243 if not o.url or o.url is None:
244 self.deactivate_app(o, onos_url, onos_basic_auth)
245 # uninstall an app from a remote source, only if it has been activated before
246 if o.url and o.url is not None:
247 self.uninstall_app(o, onos_url, onos_basic_auth)