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 nsd 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 |
from os.path import basename |
27 |
1 |
import logging |
28 |
1 |
import os.path |
29 |
|
|
30 |
|
|
31 |
1 |
class Nsd(object): |
32 |
1 |
def __init__(self, http=None, client=None): |
33 |
0 |
self._http = http |
34 |
0 |
self._client = client |
35 |
0 |
self._logger = logging.getLogger("osmclient") |
36 |
0 |
self._apiName = "/nsd" |
37 |
0 |
self._apiVersion = "/v1" |
38 |
0 |
self._apiResource = "/ns_descriptors" |
39 |
0 |
self._apiBase = "{}{}{}".format( |
40 |
|
self._apiName, self._apiVersion, self._apiResource |
41 |
|
) |
42 |
|
|
43 |
1 |
def list(self, filter=None): |
44 |
0 |
self._logger.debug("") |
45 |
0 |
self._client.get_token() |
46 |
0 |
filter_string = "" |
47 |
0 |
if filter: |
48 |
0 |
filter_string = "?{}".format(filter) |
49 |
0 |
_, resp = self._http.get2_cmd("{}{}".format(self._apiBase, filter_string)) |
50 |
|
|
51 |
0 |
if resp: |
52 |
0 |
return json.loads(resp) |
53 |
0 |
return list() |
54 |
|
|
55 |
1 |
def get(self, name): |
56 |
0 |
self._logger.debug("") |
57 |
0 |
self._client.get_token() |
58 |
0 |
if utils.validate_uuid4(name): |
59 |
0 |
for nsd in self.list(): |
60 |
0 |
if name == nsd["_id"]: |
61 |
0 |
return nsd |
62 |
|
else: |
63 |
0 |
for nsd in self.list(): |
64 |
0 |
if "name" in nsd and name == nsd["name"]: |
65 |
0 |
return nsd |
66 |
0 |
raise NotFound("nsd {} not found".format(name)) |
67 |
|
|
68 |
1 |
def get_individual(self, name): |
69 |
0 |
self._logger.debug("") |
70 |
|
# Call to get_token not required, because will be implicitly called by get. |
71 |
0 |
try: |
72 |
0 |
nsd = self.get(name) |
73 |
|
# It is redundant, since the previous one already gets the whole nsdinfo |
74 |
|
# The only difference is that a different primitive is exercised |
75 |
0 |
_, resp = self._http.get2_cmd("{}/{}".format(self._apiBase, nsd["_id"])) |
76 |
0 |
if resp: |
77 |
0 |
return json.loads(resp) |
78 |
0 |
except NotFound: |
79 |
0 |
raise NotFound("nsd '{}' not found".format(name)) |
80 |
0 |
raise NotFound("nsd '{}' not found".format(name)) |
81 |
|
|
82 |
1 |
def get_thing(self, name, thing, filename): |
83 |
0 |
self._logger.debug("") |
84 |
|
# Call to get_token not required, because will be implicitly called by get. |
85 |
0 |
nsd = self.get(name) |
86 |
0 |
headers = self._client._headers |
87 |
0 |
headers["Accept"] = "application/binary" |
88 |
0 |
http_code, resp = self._http.get2_cmd( |
89 |
|
"{}/{}/{}".format(self._apiBase, nsd["_id"], thing) |
90 |
|
) |
91 |
|
|
92 |
0 |
if resp: |
93 |
0 |
return json.loads(resp) |
94 |
|
else: |
95 |
0 |
msg = resp or "" |
96 |
0 |
raise ClientException( |
97 |
|
"failed to get {} from {} - {}".format(thing, name, msg) |
98 |
|
) |
99 |
|
|
100 |
1 |
def get_descriptor(self, name, filename): |
101 |
0 |
self._logger.debug("") |
102 |
0 |
self.get_thing(name, "nsd", filename) |
103 |
|
|
104 |
1 |
def get_package(self, name, filename): |
105 |
0 |
self._logger.debug("") |
106 |
0 |
self.get_thing(name, "package_content", filename) |
107 |
|
|
108 |
1 |
def get_artifact(self, name, artifact, filename): |
109 |
0 |
self._logger.debug("") |
110 |
0 |
self.get_thing(name, "artifacts/{}".format(artifact), filename) |
111 |
|
|
112 |
1 |
def delete(self, name, force=False): |
113 |
0 |
self._logger.debug("") |
114 |
0 |
nsd = self.get(name) |
115 |
0 |
querystring = "" |
116 |
0 |
if force: |
117 |
0 |
querystring = "?FORCE=True" |
118 |
0 |
http_code, resp = self._http.delete_cmd( |
119 |
|
"{}/{}{}".format(self._apiBase, nsd["_id"], querystring) |
120 |
|
) |
121 |
|
|
122 |
0 |
if http_code == 202: |
123 |
0 |
print("Deletion in progress") |
124 |
0 |
elif http_code == 204: |
125 |
0 |
print("Deleted") |
126 |
|
else: |
127 |
0 |
msg = resp or "" |
128 |
0 |
raise ClientException("failed to delete nsd {} - {}".format(name, msg)) |
129 |
|
|
130 |
1 |
def create( |
131 |
|
self, filename, overwrite=None, update_endpoint=None, skip_charm_build=False |
132 |
|
): |
133 |
0 |
self._logger.debug("") |
134 |
0 |
if os.path.isdir(filename): |
135 |
0 |
filename = filename.rstrip("/") |
136 |
0 |
filename = self._client.package_tool.build( |
137 |
|
filename, skip_validation=False, skip_charm_build=skip_charm_build |
138 |
|
) |
139 |
0 |
self.create(filename, overwrite=overwrite, update_endpoint=update_endpoint) |
140 |
|
else: |
141 |
0 |
self._client.get_token() |
142 |
0 |
mime_type = magic.from_file(filename, mime=True) |
143 |
0 |
if mime_type is None: |
144 |
0 |
raise ClientException( |
145 |
|
"Unexpected MIME type for file {}: MIME type {}".format( |
146 |
|
filename, mime_type |
147 |
|
) |
148 |
|
) |
149 |
0 |
headers = self._client._headers |
150 |
0 |
headers["Content-Filename"] = basename(filename) |
151 |
0 |
if mime_type in ["application/yaml", "text/plain", "application/json"]: |
152 |
0 |
headers["Content-Type"] = "text/plain" |
153 |
0 |
elif mime_type in ["application/gzip", "application/x-gzip"]: |
154 |
0 |
headers["Content-Type"] = "application/gzip" |
155 |
0 |
elif mime_type in ["application/zip"]: |
156 |
0 |
headers["Content-Type"] = "application/zip" |
157 |
|
else: |
158 |
0 |
raise ClientException( |
159 |
|
"Unexpected MIME type for file {}: MIME type {}".format( |
160 |
|
filename, mime_type |
161 |
|
) |
162 |
|
) |
163 |
0 |
headers["Content-File-MD5"] = utils.md5(filename) |
164 |
0 |
http_header = [ |
165 |
|
"{}: {}".format(key, val) for (key, val) in list(headers.items()) |
166 |
|
] |
167 |
0 |
self._http.set_http_header(http_header) |
168 |
0 |
if update_endpoint: |
169 |
0 |
http_code, resp = self._http.put_cmd( |
170 |
|
endpoint=update_endpoint, filename=filename |
171 |
|
) |
172 |
|
else: |
173 |
0 |
ow_string = "" |
174 |
0 |
if overwrite: |
175 |
0 |
ow_string = "?{}".format(overwrite) |
176 |
0 |
self._apiResource = "/ns_descriptors_content" |
177 |
0 |
self._apiBase = "{}{}{}".format( |
178 |
|
self._apiName, self._apiVersion, self._apiResource |
179 |
|
) |
180 |
0 |
endpoint = "{}{}".format(self._apiBase, ow_string) |
181 |
0 |
http_code, resp = self._http.post_cmd( |
182 |
|
endpoint=endpoint, filename=filename |
183 |
|
) |
184 |
|
|
185 |
0 |
if http_code in (200, 201, 202): |
186 |
0 |
if resp: |
187 |
0 |
resp = json.loads(resp) |
188 |
0 |
if not resp or "id" not in resp: |
189 |
0 |
raise ClientException( |
190 |
|
"unexpected response from server - {}".format(resp) |
191 |
|
) |
192 |
0 |
print(resp["id"]) |
193 |
0 |
elif http_code == 204: |
194 |
0 |
print("Updated") |
195 |
|
|
196 |
1 |
def update(self, name, filename): |
197 |
0 |
self._logger.debug("") |
198 |
0 |
nsd = self.get(name) |
199 |
0 |
endpoint = "{}/{}/nsd_content".format(self._apiBase, nsd["_id"]) |
200 |
0 |
self.create(filename=filename, update_endpoint=endpoint) |