Revert "Full Juju Charm support"
[osm/SO.git] / rwlaunchpad / plugins / rwpkgmgr / rift / tasklets / rwpkgmgr / downloader / copy.py
1 #
2 # Copyright 2017 RIFT.IO Inc
3 #
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
7 #
8 # http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15 #
16 # Author(s): Nandan Sinha
17 #
18
19 import enum
20 import gi
21 import json
22 import os
23 import shutil
24 import uuid
25
26 gi.require_version('RwVnfdYang', '1.0')
27 gi.require_version('RwNsdYang', '1.0')
28 from gi.repository import (
29 RwYang,
30 NsdYang,
31 VnfdYang,
32 RwVnfdYang,
33 RwNsdYang,
34 RwPkgMgmtYang
35 )
36
37 import rift.package.icon as icon
38 import rift.tasklets.rwlaunchpad.onboard as onboard
39
40 class PackageCopyError(Exception):
41 pass
42
43 class CopyStatus(enum.Enum):
44 UNINITIATED = 0
45 STARTED = 1
46 IN_PROGRESS = 2
47 COMPLETED = 3
48 FAILED = 4
49 CANCELLED = 5
50
51 TaskStatus = RwPkgMgmtYang.TaskStatus
52
53 class CopyMeta:
54 STATUS_MAP = {
55 CopyStatus.STARTED: TaskStatus.QUEUED.value_nick.upper(),
56 CopyStatus.UNINITIATED: TaskStatus.QUEUED.value_nick.upper(),
57 CopyStatus.IN_PROGRESS: TaskStatus.IN_PROGRESS.value_nick.upper(),
58 CopyStatus.COMPLETED: TaskStatus.COMPLETED.value_nick.upper(),
59 CopyStatus.FAILED: TaskStatus.FAILED.value_nick.upper(),
60 CopyStatus.CANCELLED: TaskStatus.CANCELLED.value_nick.upper()
61 }
62
63 def __init__(self, transaction_id):
64 self.transaction_id = transaction_id
65 self.state = CopyStatus.UNINITIATED
66
67 def set_state(self, state):
68 self.state = state
69
70 def as_dict(self):
71 return self.__dict__
72
73 def to_yang(self):
74 job = RwPkgMgmtYang.YangData_RwProject_Project_CopyJobs_Job.from_dict({
75 "transaction_id": self.transaction_id,
76 "status": CopyMeta.STATUS_MAP[self.state]
77 })
78 return job
79
80 class CopyManifest:
81 """ Utility class to hold manifest information."""
82 def __init__(self, project, log):
83 self.tasklet_info = project.tasklet.tasklet_info
84 self.manifest = self.tasklet_info.get_pb_manifest()
85 self.use_ssl = self.manifest.bootstrap_phase.rwsecurity.use_ssl
86 self.ssl_cert, self.ssl_key = None, None
87 if self.use_ssl:
88 self.ssl_cert = self.manifest.bootstrap_phase.rwsecurity.cert
89 self.ssl_key = self.manifest.bootstrap_phase.rwsecurity.key
90 self.onboarder = None
91 self.log = log
92
93 def ssl_manifest(self):
94 return (self.use_ssl, self.ssl_cert, self.ssl_key)
95
96 def get_onboarder(self, host="127.0.0.1", port="8008"):
97 if not self.onboarder:
98 self.onboarder = onboard.DescriptorOnboarder(self.log,
99 host, port, *self.ssl_manifest())
100 return self.onboarder
101
102
103 class PackageFileCopier:
104 DESCRIPTOR_MAP = {
105 "vnfd": (RwVnfdYang.YangData_Vnfd_VnfdCatalog_Vnfd, 'vnfd rw-vnfd'),
106 "nsd" : (RwNsdYang.YangData_Nsd_NsdCatalog_Nsd, 'nsd rw-nsd')
107 }
108
109 @classmethod
110 def from_rpc_input(cls, rpc_input, project, proxy, log=None):
111 return cls(
112 rpc_input.package_id,
113 rpc_input.package_type,
114 rpc_input.package_name,
115 rpc_input.project_name,
116 project = project,
117 proxy = proxy,
118 log=log)
119
120 def __init__(self,
121 pkg_id,
122 pkg_type,
123 pkg_name,
124 proj_name,
125 project,
126 proxy,
127 log):
128 self.src_package_id = pkg_id
129 self.package_type = pkg_type.lower()
130 self.dest_package_name = pkg_name
131 self.project_name = proj_name
132 self.manifest = CopyManifest(project, log)
133 self.dest_package_id = str(uuid.uuid4())
134 self.transaction_id = str(uuid.uuid4())
135 self.proxy = proxy
136 self.log = log
137 self.meta = CopyMeta(self.transaction_id)
138 self.src_package = None
139 self.dest_desc_msg = None
140
141 @property
142 def onboarder(self):
143 """ Onboarder object to invoke REST endpoint calls."""
144 return self.manifest.get_onboarder()
145
146 @property
147 def progress(self):
148 """ Current status of operations."""
149 return self.meta.to_yang()
150
151 @property
152 def descriptor_msg(self):
153 """ Descriptor message of the generated copied descriptor."""
154 return self.dest_desc_msg
155
156 # Start of delegate calls
157 def call_delegate(self, event):
158 if not self.delegate:
159 return
160
161 getattr(self.delegate, event)(self)
162
163 def _copy_tree(self):
164 """
165 Locate directory tree of the source descriptor folder.
166 Copy directory tree to destination descriptor folder.
167
168 """
169 self.copy_progress()
170
171 store = self.proxy._get_store(self.package_type, \
172 self.project_name if self.project_name else None)
173 src_path = store._get_package_dir(self.src_package_id)
174 self.src_package = store.get_package(self.src_package_id)
175
176 self.dest_copy_path = os.path.join(
177 store.root_dir,
178 self.dest_package_id)
179 self.log.debug("Copying contents from {src} to {dest}".
180 format(src=src_path, dest=self.dest_copy_path))
181
182 shutil.copytree(src_path, self.dest_copy_path)
183 # If there are icon files, also need to copy them in UI location
184 if os.path.exists(os.path.join(src_path, "icons")):
185 src_icon_path = os.path.join(
186 icon.PackageIconExtractor.DEFAULT_INSTALL_DIR,
187 self.package_type,
188 self.src_package_id)
189 dest_icon_path = os.path.join(
190 os.path.dirname(src_icon_path),
191 self.dest_package_id)
192
193 self.log.debug("Copying UI icon location from {} to {}".format(src_icon_path,
194 dest_icon_path))
195 shutil.copytree(src_icon_path, dest_icon_path)
196
197 def _create_descriptor_file(self):
198 """ Update descriptor file for the newly copied descriptor catalog.
199 Get descriptor contents from REST endpoint, change some identifiers
200 and create a new descriptor yaml file from it.
201 """
202 # API call for the updated descriptor contents
203 src_desc_contents = self.onboarder.get_updated_descriptor(self.src_package.descriptor_msg, self.project_name)
204
205 # To generate the pb object, extract subtree in dict from "project-nsd:nsd" and root it
206 # under "nsd:nsd-catalog" (or vnfd)
207 root_element = "{0}:{0}-catalog".format(self.package_type)
208 extract_sub_element = "project-{0}:{0}".format(self.package_type, self.package_type)
209 src_desc_contents[extract_sub_element].update(
210 id =self.dest_package_id,
211 name = self.dest_package_name,
212 short_name = self.dest_package_name
213 )
214 D = {}
215 D[root_element] = {self.package_type : src_desc_contents[extract_sub_element]}
216
217 # Build the proto-buf gi object from generated JSON
218 json_desc_msg = json.dumps(D)
219 self.log.debug("*** JSON contents: {}".format(json_desc_msg))
220 desc_cls, modules = PackageFileCopier.DESCRIPTOR_MAP[self.package_type]
221
222 model = RwYang.Model.create_libyang()
223 for module in modules.split():
224 model.load_module(module)
225
226 self.dest_desc_msg = desc_cls.from_json(model, json_desc_msg, strict=False)
227
228 # Write to yaml desc file
229 dest_desc_path = os.path.join(self.dest_copy_path,
230 "{pkg_name}_{pkg_type}.yaml".format(pkg_name=self.dest_package_name, pkg_type=self.package_type))
231 with open(dest_desc_path, "w") as fh:
232 fh.write(self.dest_desc_msg.to_yaml(model))
233
234 # Remove copied .yaml, if present
235 src_desc_file = self.src_package.descriptor_file
236 copied_desc_file = os.path.join(self.dest_copy_path, os.path.basename(src_desc_file))
237 if os.path.exists(copied_desc_file):
238 self.log.debug("Deleting copied yaml from old source %s" % (copied_desc_file))
239 os.remove(copied_desc_file)
240
241 def copy(self):
242 try:
243 if self.package_type not in PackageFileCopier.DESCRIPTOR_MAP:
244 raise PackageCopyError("Package type {} not currently supported for copy operations".format(self.package_type))
245
246 self._copy_tree()
247 self._create_descriptor_file()
248 self.copy_succeeded()
249
250 except Exception as e:
251 self.log.exception(str(e))
252 self.copy_failed()
253
254 self.copy_finished()
255
256 def copy_failed(self):
257 self.meta.set_state(CopyStatus.FAILED)
258 self.call_delegate("on_download_failed")
259
260 def copy_progress(self):
261 self.meta.set_state(CopyStatus.IN_PROGRESS)
262 self.call_delegate("on_download_progress")
263
264 def copy_succeeded(self):
265 self.meta.set_state(CopyStatus.COMPLETED)
266 self.call_delegate("on_download_succeeded")
267
268 def copy_finished(self):
269 self.call_delegate("on_download_finished")
270