Remove unnecessary references to fullclassname after removal of v1 client
[osm/osmclient.git] / osmclient / cli_commands / ns.py
1 # Copyright ETSI Contributors and Others.
2 # All Rights Reserved.
3 #
4 # Licensed under the Apache License, Version 2.0 (the "License"); you may
5 # not use this file except in compliance with the License. You may obtain
6 # 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, WITHOUT
12 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 # License for the specific language governing permissions and limitations
14 # under the License.
15
16 import click
17 from osmclient.common.exceptions import ClientException
18 from osmclient.common import print_output
19 from osmclient.cli_commands import utils
20 from prettytable import PrettyTable
21 import yaml
22 import json
23 from datetime import datetime
24 import logging
25
26 logger = logging.getLogger("osmclient")
27
28
29 @click.command(name="ns-list", short_help="list all NS instances")
30 @click.option(
31 "--filter",
32 default=None,
33 multiple=True,
34 help="restricts the list to the NS instances matching the filter.",
35 )
36 @click.option(
37 "--long",
38 is_flag=True,
39 help="get more details of the NS (project, vim, deployment status, configuration status.",
40 )
41 @print_output.output_option
42 @click.pass_context
43 def ns_list(ctx, filter, long, output):
44 """list all NS instances
45
46 \b
47 Options:
48 --filter filterExpr Restricts the list to the NS instances matching the filter
49
50 \b
51 filterExpr consists of one or more strings formatted according to "simpleFilterExpr",
52 concatenated using the "&" character:
53
54 \b
55 filterExpr := <simpleFilterExpr>["&"<simpleFilterExpr>]*
56 simpleFilterExpr := <attrName>["."<attrName>]*["."<op>]"="<value>[","<value>]*
57 op := "eq" | "neq" | "gt" | "lt" | "gte" | "lte" | "cont" | "ncont"
58 attrName := string
59 value := scalar value
60
61 \b
62 where:
63 * zero or more occurrences
64 ? zero or one occurrence
65 [] grouping of expressions to be used with ? and *
66 "" quotation marks for marking string constants
67 <> name separator
68
69 \b
70 "AttrName" is the name of one attribute in the data type that defines the representation
71 of the resource. The dot (".") character in "simpleFilterExpr" allows concatenation of
72 <attrName> entries to filter by attributes deeper in the hierarchy of a structured document.
73 "Op" stands for the comparison operator. If the expression has concatenated <attrName>
74 entries, it means that the operator "op" is applied to the attribute addressed by the last
75 <attrName> entry included in the concatenation. All simple filter expressions are combined
76 by the "AND" logical operator. In a concatenation of <attrName> entries in a <simpleFilterExpr>,
77 the rightmost "attrName" entry in a "simpleFilterExpr" is called "leaf attribute". The
78 concatenation of all "attrName" entries except the leaf attribute is called the "attribute
79 prefix". If an attribute referenced in an expression is an array, an object that contains a
80 corresponding array shall be considered to match the expression if any of the elements in the
81 array matches all expressions that have the same attribute prefix.
82
83 \b
84 Filter examples:
85 --filter admin-status=ENABLED
86 --filter nsd-ref=<NSD_NAME>
87 --filter nsd.vendor=<VENDOR>
88 --filter nsd.vendor=<VENDOR>&nsd-ref=<NSD_NAME>
89 --filter nsd.constituent-vnfd.vnfd-id-ref=<VNFD_NAME>
90 """
91
92 def summarize_deployment_status(status_dict):
93 # Nets
94 summary = ""
95 if not status_dict:
96 return summary
97 n_nets = 0
98 status_nets = {}
99 net_list = status_dict.get("nets", [])
100 for net in net_list:
101 n_nets += 1
102 if net["status"] not in status_nets:
103 status_nets[net["status"]] = 1
104 else:
105 status_nets[net["status"]] += 1
106 message = "Nets: "
107 for k, v in status_nets.items():
108 message += "{}:{},".format(k, v)
109 message += "TOTAL:{}".format(n_nets)
110 summary += "{}".format(message)
111 # VMs and VNFs
112 n_vms = 0
113 status_vms = {}
114 status_vnfs = {}
115 vnf_list = status_dict["vnfs"]
116 for vnf in vnf_list:
117 member_vnf_index = vnf["member_vnf_index"]
118 if member_vnf_index not in status_vnfs:
119 status_vnfs[member_vnf_index] = {}
120 for vm in vnf["vms"]:
121 n_vms += 1
122 if vm["status"] not in status_vms:
123 status_vms[vm["status"]] = 1
124 else:
125 status_vms[vm["status"]] += 1
126 if vm["status"] not in status_vnfs[member_vnf_index]:
127 status_vnfs[member_vnf_index][vm["status"]] = 1
128 else:
129 status_vnfs[member_vnf_index][vm["status"]] += 1
130 message = "VMs: "
131 for k, v in status_vms.items():
132 message += "{}:{},".format(k, v)
133 message += "TOTAL:{}".format(n_vms)
134 summary += "\n{}".format(message)
135 summary += "\nNFs:"
136 for k, v in status_vnfs.items():
137 total = 0
138 message = "\n {} VMs: ".format(k)
139 for k2, v2 in v.items():
140 message += "{}:{},".format(k2, v2)
141 total += v2
142 message += "TOTAL:{}".format(total)
143 summary += message
144 return summary
145
146 def summarize_config_status(ee_list):
147 summary = ""
148 if not ee_list:
149 return summary
150 n_ee = 0
151 status_ee = {}
152 for ee in ee_list:
153 n_ee += 1
154 if ee["elementType"] not in status_ee:
155 status_ee[ee["elementType"]] = {}
156 status_ee[ee["elementType"]][ee["status"]] = 1
157 continue
158 if ee["status"] in status_ee[ee["elementType"]]:
159 status_ee[ee["elementType"]][ee["status"]] += 1
160 else:
161 status_ee[ee["elementType"]][ee["status"]] = 1
162 for elementType in ["KDU", "VDU", "PDU", "VNF", "NS"]:
163 if elementType in status_ee:
164 message = ""
165 total = 0
166 for k, v in status_ee[elementType].items():
167 message += "{}:{},".format(k, v)
168 total += v
169 message += "TOTAL:{}\n".format(total)
170 summary += "{}: {}".format(elementType, message)
171 summary += "TOTAL Exec. Env.: {}".format(n_ee)
172 return summary
173
174 logger.debug("")
175 if filter:
176 utils.check_client_version(ctx.obj, "--filter")
177 filter = "&".join(filter)
178 resp = ctx.obj.ns.list(filter)
179 else:
180 resp = ctx.obj.ns.list()
181 if long:
182 table = PrettyTable(
183 [
184 "ns instance name",
185 "id",
186 "date",
187 "ns state",
188 "current operation",
189 "error details",
190 "project",
191 "vim (inst param)",
192 "deployment status",
193 "configuration status",
194 ]
195 )
196 project_list = ctx.obj.project.list()
197 try:
198 vim_list = ctx.obj.vim.list()
199 except Exception:
200 vim_list = []
201 else:
202 table = PrettyTable(
203 [
204 "ns instance name",
205 "id",
206 "date",
207 "ns state",
208 "current operation",
209 "error details",
210 ]
211 )
212 for ns in resp:
213 nsr = ns
214 logger.debug("NS info: {}".format(nsr))
215 nsr_name = nsr["name"]
216 nsr_id = nsr["_id"]
217 date = datetime.fromtimestamp(nsr["create-time"]).strftime("%Y-%m-%dT%H:%M:%S")
218 ns_state = nsr.get("nsState", nsr["_admin"]["nsState"])
219 if long:
220 deployment_status = summarize_deployment_status(nsr.get("deploymentStatus"))
221 config_status = summarize_config_status(nsr.get("configurationStatus"))
222 project_id, project_name = utils.get_project(project_list, nsr)
223 # project = '{} ({})'.format(project_name, project_id)
224 project = project_name
225 vim_id = nsr.get("datacenter")
226 vim_name = utils.get_vim_name(vim_list, vim_id)
227
228 # vim = '{} ({})'.format(vim_name, vim_id)
229 vim = vim_name
230 if "currentOperation" in nsr:
231 current_operation = "{} ({})".format(
232 nsr["currentOperation"], nsr["currentOperationID"]
233 )
234 else:
235 current_operation = "{} ({})".format(
236 nsr["_admin"].get("current-operation", "-"),
237 nsr["_admin"]["nslcmop"],
238 )
239 error_details = "N/A"
240 if (
241 ns_state == "BROKEN"
242 or ns_state == "DEGRADED"
243 or ("currentOperation" not in nsr and nsr.get("errorDescription"))
244 ):
245 error_details = "{}\nDetail: {}".format(
246 nsr["errorDescription"], nsr["errorDetail"]
247 )
248
249 if long:
250 table.add_row(
251 [
252 nsr_name,
253 nsr_id,
254 date,
255 ns_state,
256 current_operation,
257 utils.wrap_text(text=error_details, width=40),
258 project,
259 vim,
260 deployment_status,
261 config_status,
262 ]
263 )
264 else:
265 table.add_row(
266 [
267 nsr_name,
268 nsr_id,
269 date,
270 ns_state,
271 current_operation,
272 utils.wrap_text(text=error_details, width=40),
273 ]
274 )
275 print_output.print_output(output, table.field_names, table._rows)
276 print('To get the history of all operations over a NS, run "osm ns-op-list NS_ID"')
277 print(
278 'For more details on the current operation, run "osm ns-op-show OPERATION_ID"'
279 )
280
281
282 @click.command(name="ns-show", short_help="shows the info of a NS instance")
283 @click.argument("name")
284 @click.option("--literal", is_flag=True, help="print literally, no pretty table")
285 @click.option(
286 "--filter",
287 multiple=True,
288 help="restricts the information to the fields in the filter",
289 )
290 @print_output.output_option
291 @click.pass_context
292 def ns_show(ctx, name, literal, filter, output):
293 """shows the info of a NS instance
294
295 NAME: name or ID of the NS instance
296 """
297 logger.debug("")
298 ns = ctx.obj.ns.get(name)
299
300 if literal:
301 print(yaml.safe_dump(ns, indent=4, default_flow_style=False))
302 return
303
304 table = PrettyTable(["field", "value"])
305
306 for k, v in list(ns.items()):
307 if not filter or k in filter:
308 table.add_row([k, utils.wrap_text(text=json.dumps(v, indent=2), width=100)])
309
310 nsopdata = ctx.obj.ns.get_opdata(ns["id"])
311 nsr_optdata = nsopdata["nsr:nsr"]
312 for k, v in list(nsr_optdata.items()):
313 if not filter or k in filter:
314 table.add_row([k, utils.wrap_text(json.dumps(v, indent=2), width=100)])
315 print_output.print_output(output, table.field_names, table._rows)
316
317
318 @click.command(name="ns-create", short_help="creates a new Network Service instance")
319 @click.option("--ns_name", prompt=True, help="name of the NS instance")
320 @click.option("--nsd_name", prompt=True, help="name of the NS descriptor")
321 @click.option(
322 "--vim_account",
323 prompt=True,
324 help="default VIM account id or name for the deployment",
325 )
326 @click.option("--admin_status", default="ENABLED", help="administration status")
327 @click.option(
328 "--ssh_keys",
329 default=None,
330 help="comma separated list of public key files to inject to vnfs",
331 )
332 @click.option("--config", default=None, help="ns specific yaml configuration")
333 @click.option("--config_file", default=None, help="ns specific yaml configuration file")
334 @click.option(
335 "--wait",
336 required=False,
337 default=False,
338 is_flag=True,
339 help="do not return the control immediately, but keep it "
340 "until the operation is completed, or timeout",
341 )
342 @click.option("--timeout", default=None, help="ns deployment timeout")
343 @click.pass_context
344 def ns_create(
345 ctx,
346 nsd_name,
347 ns_name,
348 vim_account,
349 admin_status,
350 ssh_keys,
351 config,
352 config_file,
353 wait,
354 timeout,
355 ):
356 """creates a new NS instance"""
357 logger.debug("")
358 if config_file:
359 utils.check_client_version(ctx.obj, "--config_file")
360 if config:
361 raise ClientException(
362 '"--config" option is incompatible with "--config_file" option'
363 )
364 with open(config_file, "r") as cf:
365 config = cf.read()
366 ctx.obj.ns.create(
367 nsd_name,
368 ns_name,
369 config=config,
370 ssh_keys=ssh_keys,
371 account=vim_account,
372 wait=wait,
373 timeout=timeout,
374 )
375
376
377 @click.command(name="ns-delete", short_help="deletes a NS instance")
378 @click.argument("name")
379 @click.option(
380 "--force", is_flag=True, help="forces the deletion bypassing pre-conditions"
381 )
382 @click.option(
383 "--config",
384 default=None,
385 help="specific yaml configuration for the termination, e.g. '{autoremove: False, timeout_ns_terminate: "
386 "600, skip_terminate_primitives: True}'",
387 )
388 @click.option(
389 "--wait",
390 required=False,
391 default=False,
392 is_flag=True,
393 help="do not return the control immediately, but keep it "
394 "until the operation is completed, or timeout",
395 )
396 @click.pass_context
397 def ns_delete(ctx, name, force, config, wait):
398 """deletes a NS instance
399
400 NAME: name or ID of the NS instance to be deleted
401 """
402 logger.debug("")
403 if not force:
404 ctx.obj.ns.delete(name, config=config, wait=wait)
405 else:
406 utils.check_client_version(ctx.obj, "--force")
407 ctx.obj.ns.delete(name, force, config=config, wait=wait)