blob: cc30645cb3f5b4a00abb4771a4bdece6c9a96644 [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 """
44 for dep in [x.strip() for x in str(deps).split(',') if x is not ""]:
45 try:
46 app = ONOSApp.objects.get(app_id=dep)
47 if not app.backend_code == 1:
48 # backend_code == 1 means that the app has been pushed
49 return False
50 except IndexError, e:
51 return False
52 return True
53
54 def add_config(self, o):
55 log.info("Adding config %s" % o.name, model=o.tologdict())
56 # getting onos url and auth
57 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)
58 onos_basic_auth = HTTPBasicAuth(o.service_instance.leaf_model.owner.leaf_model.rest_username, o.service_instance.leaf_model.owner.leaf_model.rest_password)
59
60 # push configs (if any)
61 url = o.name
62 if url[0] == "/":
63 # strip initial /
64 url = url[1:]
65
66 url = '%s/%s' % (onos_url, url)
67 value = json.loads(o.value)
68 request = requests.post(url, json=value, auth=onos_basic_auth)
69
70 if request.status_code != 200:
71 log.error("Request failed", response=request.text)
72 raise Exception("Failed to add config %s in ONOS: %s" % (url, request.text))
73
74 def activate_app(self, o, onos_url, onos_basic_auth):
75 log.info("Activating app %s" % o.app_id)
76 url = '%s/onos/v1/applications/%s/active' % (onos_url, o.app_id)
77 request = requests.post(url, auth=onos_basic_auth)
78
79 if request.status_code != 200:
80 log.error("Request failed", response=request.text)
81 raise Exception("Failed to add application %s to ONOS: %s" % (url, request.text))
82
83 url = '%s/onos/v1/applications/%s' % (onos_url, o.app_id)
84 request = requests.get(url, auth=onos_basic_auth)
85
86 if request.status_code != 200:
87 log.error("Request failed", response=request.text)
88 raise Exception("Failed to read application %s from ONOS: %s" % (url, request.text))
89 else:
90 o.version = request.json()["version"]
91
92 def check_app_installed(self, o, onos_url, onos_basic_auth):
Matteo Scandolo3b672572018-06-25 14:28:36 -070093 log.debug("Checking if app is installed", app=o.app_id)
Matteo Scandolo0b986e22018-06-04 14:07:33 -070094 url = '%s/onos/v1/applications/%s' % (onos_url, o.app_id)
95 request = requests.get(url, auth=onos_basic_auth)
96
97 if request.status_code == 200:
98 if "version" in request.json() and o.version == request.json()["version"]:
Matteo Scandolo3b672572018-06-25 14:28:36 -070099 log.debug("App is installed", app=o.app_id)
Matteo Scandolo0b986e22018-06-04 14:07:33 -0700100 return True
101 else:
102 # uninstall the application
103 self.uninstall_app(o, onos_url, onos_basic_auth)
104 return False
105 if request.status_code == 404:
106 # app is not installed at all
107 return False
108 else:
109 log.error("Request failed", response=request.text)
Matteo Scandolo3b672572018-06-25 14:28:36 -0700110 raise Exception("Failed to read application %s from ONOS aaa: %s" % (url, request.text))
Matteo Scandolo0b986e22018-06-04 14:07:33 -0700111
112 def install_app(self, o, onos_url, onos_basic_auth):
Matteo Scandolo3b672572018-06-25 14:28:36 -0700113 log.info("Installing app from url %s" % o.url, app=o.app_id, version=o.version)
Matteo Scandolo0b986e22018-06-04 14:07:33 -0700114
Matteo Scandolo3b672572018-06-25 14:28:36 -0700115 # check is the already installed app is the correct version
116 is_installed = self.check_app_installed(o, onos_url, onos_basic_auth)
Matteo Scandolo0b986e22018-06-04 14:07:33 -0700117
118 if is_installed:
119 # if the app is already installed we don't need to do anything
Matteo Scandolo3b672572018-06-25 14:28:36 -0700120 log.info("App is installed, skipping install", app=o.app_id)
Matteo Scandolo0b986e22018-06-04 14:07:33 -0700121 return
122
Matteo Scandolo0b986e22018-06-04 14:07:33 -0700123 data = {
124 'activate': True,
125 'url': o.url
126 }
127 url = '%s/onos/v1/applications' % onos_url
128 request = requests.post(url, json=data, auth=onos_basic_auth)
129
Matteo Scandolo3b672572018-06-25 14:28:36 -0700130 if request.status_code == 409:
131 log.info("App was already installed", app=o.app_id, test=request.text)
132 return
133
Matteo Scandolo0b986e22018-06-04 14:07:33 -0700134 if request.status_code != 200:
135 log.error("Request failed", response=request.text)
136 raise Exception("Failed to add application %s to ONOS: %s" % (url, request.text))
137
Matteo Scandolo3b672572018-06-25 14:28:36 -0700138 log.debug("App from url %s installed" % o.url, app=o.app_id, version=o.version)
Matteo Scandolo0b986e22018-06-04 14:07:33 -0700139
140 url = '%s/onos/v1/applications/%s' % (onos_url, o.app_id)
141 request = requests.get(url, auth=onos_basic_auth)
142
143 if request.status_code != 200:
144 log.error("Request failed", response=request.text)
Matteo Scandolo3b672572018-06-25 14:28:36 -0700145 raise Exception("Failed to read application %s from ONOS: %s while checking correct version" % (url, request.text))
Matteo Scandolo0b986e22018-06-04 14:07:33 -0700146 else:
147 if o.version != request.json()["version"]:
148 raise Exception("The version of %s you installed (%s) is not the same you requested (%s)" % (o.app_id, request.json()["version"], o.version))
149
150 def sync_record(self, o):
151 log.info("Sync'ing", model=o.tologdict())
152 if hasattr(o, 'service_instance'):
153 # this is a ServiceInstanceAttribute model just push the config
154 if 'ONOSApp' in o.service_instance.leaf_model.class_names:
155 return self.add_config(o)
156 return # if it's not an ONOSApp do nothing
157
158 if not self.check_app_dependencies(o.dependencies):
159 raise DeferredException('Deferring installation of ONOSApp with id %s as dependencies are not met' % o.id)
160
161 # getting onos url and auth
162 onos_url = "%s:%s" % (Helpers.format_url(o.owner.leaf_model.rest_hostname), o.owner.leaf_model.rest_port)
163 onos_basic_auth = HTTPBasicAuth(o.owner.leaf_model.rest_username, o.owner.leaf_model.rest_password)
164
165 # activate app (bundled in onos)
166 if not o.url or o.url is None:
167 self.activate_app(o, onos_url, onos_basic_auth)
168 # install an app from a remote source
169 if o.url and o.url is not None:
170 self.install_app(o, onos_url, onos_basic_auth)
171
172 def delete_config(self, o):
173 log.info("Deleting config %s" % o.name)
174 # getting onos url and auth
175 onos_app = o.service_instance.leaf_model
176 onos_url = "%s:%s" % (Helpers.format_url(onos_app.owner.leaf_model.rest_hostname), onos_app.owner.leaf_model.rest_port)
177 onos_basic_auth = HTTPBasicAuth(onos_app.owner.leaf_model.rest_username, onos_app.owner.leaf_model.rest_password)
178
179 url = o.name
180 if url[0] == "/":
181 # strip initial /
182 url = url[1:]
183
184 url = '%s/%s' % (onos_url, url)
185 request = requests.delete(url, auth=onos_basic_auth)
186
187 if request.status_code != 204:
188 log.error("Request failed", response=request.text)
189 raise Exception("Failed to remove config %s from ONOS: %s" % (url, request.text))
190
191 def uninstall_app(self,o, onos_url, onos_basic_auth):
192 log.info("Uninstalling app %s" % o.app_id)
193 url = '%s/onos/v1/applications/%s' % (onos_url, o.app_id)
194
195 request = requests.delete(url, auth=onos_basic_auth)
196
197 if request.status_code != 204:
198 log.error("Request failed", response=request.text)
199 raise Exception("Failed to delete application %s from ONOS: %s" % (url, request.text))
200
201 def deactivate_app(self, o, onos_url, onos_basic_auth):
202 log.info("Deactivating app %s" % o.app_id)
203 url = '%s/onos/v1/applications/%s/active' % (onos_url, o.app_id)
204
205 request = requests.delete(url, auth=onos_basic_auth)
206
207 if request.status_code != 204:
208 log.error("Request failed", response=request.text)
209 raise Exception("Failed to deactivate application %s from ONOS: %s" % (url, request.text))
210
211 def delete_record(self, o):
212
213 if hasattr(o, 'service_instance'):
214 # this is a ServiceInstanceAttribute model
215 if 'ONOSApp' in o.service_instance.leaf_model.class_names:
216 return self.delete_config(o)
217 return # if it's not related to an ONOSApp do nothing
218
219 # NOTE if it is an ONOSApp we don't care about the ServiceInstanceAttribute
220 # as the reaper will delete it
221
222 # getting onos url and auth
223 onos_url = "%s:%s" % (Helpers.format_url(o.owner.leaf_model.rest_hostname), o.owner.leaf_model.rest_port)
224 onos_basic_auth = HTTPBasicAuth(o.owner.leaf_model.rest_username, o.owner.leaf_model.rest_password)
225
226 # deactivate an app (bundled in onos)
227 if not o.url or o.url is None:
228 self.deactivate_app(o, onos_url, onos_basic_auth)
229 # uninstall an app from a remote source, only if it has been activated before
230 if o.url and o.url is not None:
231 self.uninstall_app(o, onos_url, onos_basic_auth)