1 |
|
# Copyright 2018 Telefonica |
2 |
|
# |
3 |
|
# All Rights Reserved. |
4 |
|
# |
5 |
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may |
6 |
|
# not use this file except in compliance with the License. You may obtain |
7 |
|
# a copy of the License at |
8 |
|
# |
9 |
|
# http://www.apache.org/licenses/LICENSE-2.0 |
10 |
|
# |
11 |
|
# Unless required by applicable law or agreed to in writing, software |
12 |
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
13 |
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
14 |
|
# License for the specific language governing permissions and limitations |
15 |
|
# under the License. |
16 |
|
|
17 |
1 |
""" |
18 |
|
OSM NST (Network Slice Template) API handling |
19 |
|
""" |
20 |
|
|
21 |
1 |
from osmclient.common.exceptions import NotFound |
22 |
1 |
from osmclient.common.exceptions import ClientException |
23 |
1 |
from osmclient.common import utils |
24 |
1 |
import json |
25 |
1 |
import magic |
26 |
1 |
import logging |
27 |
1 |
import os.path |
28 |
|
|
29 |
|
# from os import stat |
30 |
|
# from os.path import basename |
31 |
|
|
32 |
|
|
33 |
1 |
class Nst(object): |
34 |
1 |
def __init__(self, http=None, client=None): |
35 |
0 |
self._http = http |
36 |
0 |
self._client = client |
37 |
0 |
self._logger = logging.getLogger("osmclient") |
38 |
0 |
self._apiName = "/nst" |
39 |
0 |
self._apiVersion = "/v1" |
40 |
0 |
self._apiResource = "/netslice_templates" |
41 |
0 |
self._apiBase = "{}{}{}".format( |
42 |
|
self._apiName, self._apiVersion, self._apiResource |
43 |
|
) |
44 |
|
|
45 |
1 |
def list(self, filter=None): |
46 |
0 |
self._logger.debug("") |
47 |
0 |
self._client.get_token() |
48 |
0 |
filter_string = "" |
49 |
0 |
if filter: |
50 |
0 |
filter_string = "?{}".format(filter) |
51 |
0 |
_, resp = self._http.get2_cmd("{}{}".format(self._apiBase, filter_string)) |
52 |
|
# print(yaml.safe_dump(resp)) |
53 |
0 |
if resp: |
54 |
0 |
return json.loads(resp) |
55 |
0 |
return list() |
56 |
|
|
57 |
1 |
def get(self, name): |
58 |
0 |
self._logger.debug("") |
59 |
0 |
self._client.get_token() |
60 |
0 |
if utils.validate_uuid4(name): |
61 |
0 |
for nst in self.list(): |
62 |
0 |
if name == nst["_id"]: |
63 |
0 |
return nst |
64 |
|
else: |
65 |
0 |
for nst in self.list(): |
66 |
0 |
if "name" in nst and name == nst["name"]: |
67 |
0 |
return nst |
68 |
0 |
raise NotFound("nst {} not found".format(name)) |
69 |
|
|
70 |
1 |
def get_individual(self, name): |
71 |
0 |
self._logger.debug("") |
72 |
0 |
nst = self.get(name) |
73 |
|
# It is redundant, since the previous one already gets the whole nstinfo |
74 |
|
# The only difference is that a different primitive is exercised |
75 |
0 |
try: |
76 |
0 |
_, resp = self._http.get2_cmd("{}/{}".format(self._apiBase, nst["_id"])) |
77 |
|
# print(yaml.safe_dump(resp)) |
78 |
0 |
if resp: |
79 |
0 |
return json.loads(resp) |
80 |
0 |
except NotFound: |
81 |
0 |
raise NotFound("nst '{}' not found".format(name)) |
82 |
0 |
raise NotFound("nst '{}' not found".format(name)) |
83 |
|
|
84 |
1 |
def get_thing(self, name, thing, filename): |
85 |
0 |
self._logger.debug("") |
86 |
0 |
nst = self.get(name) |
87 |
0 |
headers = self._client._headers |
88 |
0 |
headers["Accept"] = "application/binary" |
89 |
0 |
try: |
90 |
0 |
http_code, resp = self._http.get2_cmd( |
91 |
|
"{}/{}/{}".format(self._apiBase, nst["_id"], thing) |
92 |
|
) |
93 |
0 |
except NotFound: |
94 |
0 |
raise NotFound("nst '{} 'not found".format(name)) |
95 |
|
# print('HTTP CODE: {}'.format(http_code)) |
96 |
|
# print('RESP: {}'.format(resp)) |
97 |
|
# if http_code in (200, 201, 202, 204): |
98 |
0 |
if resp: |
99 |
|
# store in a file |
100 |
0 |
return json.loads(resp) |
101 |
|
# else: |
102 |
|
# msg = "" |
103 |
|
# if resp: |
104 |
|
# try: |
105 |
|
# msg = json.loads(resp) |
106 |
|
# except ValueError: |
107 |
|
# msg = resp |
108 |
|
# raise ClientException("failed to get {} from {} - {}".format(thing, name, msg)) |
109 |
|
|
110 |
1 |
def get_descriptor(self, name, filename): |
111 |
0 |
self._logger.debug("") |
112 |
0 |
self.get_thing(name, "nst", filename) |
113 |
|
|
114 |
1 |
def get_package(self, name, filename): |
115 |
0 |
self._logger.debug("") |
116 |
0 |
self.get_thing(name, "nst_content", filename) |
117 |
|
|
118 |
1 |
def get_artifact(self, name, artifact, filename): |
119 |
0 |
self._logger.debug("") |
120 |
0 |
self.get_thing(name, "artifacts/{}".format(artifact), filename) |
121 |
|
|
122 |
1 |
def delete(self, name, force=False): |
123 |
0 |
self._logger.debug("") |
124 |
0 |
nst = self.get(name) |
125 |
0 |
querystring = "" |
126 |
0 |
if force: |
127 |
0 |
querystring = "?FORCE=True" |
128 |
0 |
http_code, resp = self._http.delete_cmd( |
129 |
|
"{}/{}{}".format(self._apiBase, nst["_id"], querystring) |
130 |
|
) |
131 |
|
# print('HTTP CODE: {}'.format(http_code)) |
132 |
|
# print('RESP: {}'.format(resp)) |
133 |
0 |
if http_code == 202: |
134 |
0 |
print("Deletion in progress") |
135 |
0 |
elif http_code == 204: |
136 |
0 |
print("Deleted") |
137 |
|
else: |
138 |
0 |
msg = resp or "" |
139 |
|
# if resp: |
140 |
|
# try: |
141 |
|
# resp = json.loads(resp) |
142 |
|
# except ValueError: |
143 |
|
# msg = resp |
144 |
0 |
raise ClientException("failed to delete nst {} - {}".format(name, msg)) |
145 |
|
|
146 |
1 |
def create(self, filename, overwrite=None, update_endpoint=None): |
147 |
0 |
self._logger.debug("") |
148 |
0 |
if os.path.isdir(filename): |
149 |
0 |
charm_folder = filename.rstrip("/") |
150 |
0 |
for files in os.listdir(charm_folder): |
151 |
0 |
if "nst.yaml" in files: |
152 |
0 |
results = self._client.package_tool.validate( |
153 |
|
charm_folder, recursive=False |
154 |
|
) |
155 |
0 |
for result in results: |
156 |
0 |
if result["valid"] != "OK": |
157 |
0 |
raise ClientException( |
158 |
|
"There was an error validating the file: {} " |
159 |
|
"with error: {}".format(result["path"], result["error"]) |
160 |
|
) |
161 |
0 |
result = self._client.package_tool.build(charm_folder) |
162 |
0 |
if "Created" in result: |
163 |
0 |
filename = "{}.tar.gz".format(charm_folder) |
164 |
|
else: |
165 |
0 |
raise ClientException( |
166 |
|
"Failed in {}tar.gz creation".format(charm_folder) |
167 |
|
) |
168 |
0 |
self.create(filename, overwrite, update_endpoint) |
169 |
|
else: |
170 |
0 |
self._client.get_token() |
171 |
0 |
mime_type = magic.from_file(filename, mime=True) |
172 |
0 |
if mime_type is None: |
173 |
0 |
raise ClientException( |
174 |
|
"Unexpected MIME type for file {}: MIME type {}".format( |
175 |
|
filename, mime_type |
176 |
|
) |
177 |
|
) |
178 |
0 |
headers = self._client._headers |
179 |
0 |
if mime_type in ["application/yaml", "text/plain"]: |
180 |
0 |
headers["Content-Type"] = "application/yaml" |
181 |
0 |
elif mime_type in ["application/gzip", "application/x-gzip"]: |
182 |
0 |
headers["Content-Type"] = "application/gzip" |
183 |
|
# headers['Content-Type'] = 'application/binary' |
184 |
|
# Next three lines are to be removed in next version |
185 |
|
# headers['Content-Filename'] = basename(filename) |
186 |
|
# file_size = stat(filename).st_size |
187 |
|
# headers['Content-Range'] = 'bytes 0-{}/{}'.format(file_size - 1, file_size) |
188 |
|
else: |
189 |
0 |
raise ClientException( |
190 |
|
"Unexpected MIME type for file {}: MIME type {}".format( |
191 |
|
filename, mime_type |
192 |
|
) |
193 |
|
) |
194 |
0 |
headers["Content-File-MD5"] = utils.md5(filename) |
195 |
0 |
self._http.set_http_header(headers) |
196 |
0 |
if update_endpoint: |
197 |
0 |
http_code, resp = self._http.put_cmd( |
198 |
|
endpoint=update_endpoint, filename=filename |
199 |
|
) |
200 |
|
else: |
201 |
0 |
ow_string = "" |
202 |
0 |
if overwrite: |
203 |
0 |
ow_string = "?{}".format(overwrite) |
204 |
0 |
self._apiResource = "/netslice_templates_content" |
205 |
0 |
self._apiBase = "{}{}{}".format( |
206 |
|
self._apiName, self._apiVersion, self._apiResource |
207 |
|
) |
208 |
0 |
endpoint = "{}{}".format(self._apiBase, ow_string) |
209 |
0 |
http_code, resp = self._http.post_cmd( |
210 |
|
endpoint=endpoint, filename=filename |
211 |
|
) |
212 |
|
# print('HTTP CODE: {}'.format(http_code)) |
213 |
|
# print('RESP: {}'.format(resp)) |
214 |
|
# if http_code in (200, 201, 202, 204): |
215 |
0 |
if resp: |
216 |
0 |
resp = json.loads(resp) |
217 |
0 |
if not resp or "id" not in resp: |
218 |
0 |
raise ClientException( |
219 |
|
"unexpected response from server - {}".format(resp) |
220 |
|
) |
221 |
0 |
print(resp["id"]) |
222 |
|
# else: |
223 |
|
# msg = "Error {}".format(http_code) |
224 |
|
# if resp: |
225 |
|
# try: |
226 |
|
# msg = "{} - {}".format(msg, json.loads(resp)) |
227 |
|
# except ValueError: |
228 |
|
# msg = "{} - {}".format(msg, resp) |
229 |
|
# raise ClientException("failed to create/update nst - {}".format(msg)) |
230 |
|
|
231 |
1 |
def update(self, name, filename): |
232 |
0 |
self._logger.debug("") |
233 |
0 |
nst = self.get(name) |
234 |
0 |
endpoint = "{}/{}/nst_content".format(self._apiBase, nst["_id"]) |
235 |
0 |
self.create(filename=filename, update_endpoint=endpoint) |