4 # Copyright 2016 RIFT.IO Inc
6 # Licensed under the Apache License, Version 2.0 (the "License");
7 # you may not use this file except in compliance with the License.
8 # You may obtain a copy of the License at
10 # http://www.apache.org/licenses/LICENSE-2.0
12 # Unless required by applicable law or agreed to in writing, software
13 # distributed under the License is distributed on an "AS IS" BASIS,
14 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 # See the License for the specific language governing permissions and
16 # limitations under the License.
30 class OpenmanoCommandFailed(Exception):
34 class OpenmanoUnexpectedOutput(Exception):
38 class VNFExistsError(Exception):
42 class InstanceStatusError(Exception):
46 class OpenmanoHttpAPI(object):
47 def __init__(self
, log
, host
, port
, tenant
):
53 self
._session
= requests
.Session()
55 def get_instance(self
, instance_uuid
):
56 url
= "http://{host}:{port}/openmano/{tenant}/instances/{instance}".format(
60 instance
=instance_uuid
,
63 resp
= self
._session
.get(url
)
65 resp
.raise_for_status()
66 except requests
.exceptions
.HTTPError
as e
:
67 raise InstanceStatusError(e
)
71 def get_instance_vm_console_url(self
, instance_uuid
, vim_uuid
):
72 url
= "http://{host}:{port}/openmano/{tenant}/instances/{instance}/action".format(
76 instance
=instance_uuid
,
79 console_types
= ("novnc", "spice-html5", "xvpnvc", "rdp-html5")
80 for console_type
in console_types
:
81 payload_input
= {"console":console_type
, "vms":[vim_uuid
]}
82 payload_data
= json
.dumps(payload_input
)
83 resp
= self
._session
.post(url
, headers
={'content-type': 'application/json'},
86 resp
.raise_for_status()
87 except requests
.exceptions
.HTTPError
as e
:
88 raise InstanceStatusError(e
)
90 if vim_uuid
in result
and (result
[vim_uuid
]["vim_result"] == 1 or result
[vim_uuid
]["vim_result"] == 200):
91 return result
[vim_uuid
]["description"]
96 class OpenmanoCliAPI(object):
97 """ This class implements the necessary funtionality to interact with """
101 def __init__(self
, log
, host
, port
, tenant
):
105 self
._tenant
= tenant
108 def openmano_cmd_path():
110 os
.environ
["RIFT_INSTALL"],
114 def _openmano_cmd(self
, arg_list
, expected_lines
=None):
115 cmd_args
= list(arg_list
)
116 cmd_args
.insert(0, self
.openmano_cmd_path())
119 "OPENMANO_HOST": self
._host
,
120 "OPENMANO_PORT": str(self
._port
),
121 "OPENMANO_TENANT": self
._tenant
,
125 "Running openmano command (%s) using env (%s)",
126 subprocess
.list2cmdline(cmd_args
),
130 proc
= subprocess
.Popen(
132 stdout
=subprocess
.PIPE
,
133 stderr
=subprocess
.PIPE
,
134 universal_newlines
=True,
138 stdout
, stderr
= proc
.communicate(timeout
=self
.CMD_TIMEOUT
)
139 except subprocess
.TimeoutExpired
:
140 self
._log
.error("Openmano command timed out")
142 stdout
, stderr
= proc
.communicate(timeout
=self
.CMD_TIMEOUT
)
144 if proc
.returncode
!= 0:
146 "Openmano command failed (rc=%s) with stdout: %s",
147 proc
.returncode
, stdout
149 raise OpenmanoCommandFailed(stdout
)
151 self
._log
.debug("Openmano command completed with stdout: %s", stdout
)
153 output_lines
= stdout
.splitlines()
154 if expected_lines
is not None:
155 if len(output_lines
) != expected_lines
:
156 msg
= "Expected %s lines from openmano command. Got %s" % (expected_lines
, len(output_lines
))
158 raise OpenmanoUnexpectedOutput(msg
)
163 def vnf_create(self
, vnf_yaml_str
):
164 """ Create a Openmano VNF from a Openmano VNF YAML string """
166 self
._log
.debug("Creating VNF: %s", vnf_yaml_str
)
168 with tempfile
.NamedTemporaryFile() as vnf_file_hdl
:
169 vnf_file_hdl
.write(vnf_yaml_str
.encode())
173 output_lines
= self
._openmano
_cmd
(
174 ["vnf-create", vnf_file_hdl
.name
],
177 except OpenmanoCommandFailed
as e
:
178 if "already in use" in str(e
):
179 raise VNFExistsError("VNF was already added")
182 vnf_info_line
= output_lines
[0]
183 vnf_id
, vnf_name
= vnf_info_line
.split(" ", 1)
185 self
._log
.info("VNF %s Created: %s", vnf_name
, vnf_id
)
187 return vnf_id
, vnf_name
189 def vnf_delete(self
, vnf_uuid
):
191 ["vnf-delete", vnf_uuid
, "-f"],
194 self
._log
.info("VNF Deleted: %s", vnf_uuid
)
198 output_lines
= self
._openmano
_cmd
(
202 except OpenmanoCommandFailed
as e
:
203 self
._log
.warning("Vnf listing returned an error: %s", str(e
))
207 for line
in output_lines
:
209 uuid
, name
= line
.split(" ", 1)
210 name_uuid_map
[name
.strip()] = uuid
.strip()
212 self
._log
.debug("VNF list: {}".format(name_uuid_map
))
215 def ns_create(self
, ns_yaml_str
, name
=None):
216 self
._log
.info("Creating NS: %s", ns_yaml_str
)
218 with tempfile
.NamedTemporaryFile() as ns_file_hdl
:
219 ns_file_hdl
.write(ns_yaml_str
.encode())
222 cmd_args
= ["scenario-create", ns_file_hdl
.name
]
224 cmd_args
.extend(["--name", name
])
226 output_lines
= self
._openmano
_cmd
(
231 ns_info_line
= output_lines
[0]
232 ns_id
, ns_name
= ns_info_line
.split(" ", 1)
234 self
._log
.info("NS %s Created: %s", ns_name
, ns_id
)
236 return ns_id
, ns_name
239 self
._log
.debug("Getting NS list")
242 output_lines
= self
._openmano
_cmd
(
246 except OpenmanoCommandFailed
as e
:
247 self
._log
.warning("NS listing returned an error: %s", str(e
))
251 for line
in output_lines
:
253 uuid
, name
= line
.split(" ", 1)
254 name_uuid_map
[name
.strip()] = uuid
.strip()
256 self
._log
.debug("Scenario list: {}".format(name_uuid_map
))
259 def ns_delete(self
, ns_uuid
):
260 self
._log
.info("Deleting NS: %s", ns_uuid
)
263 ["scenario-delete", ns_uuid
, "-f"],
266 self
._log
.info("NS Deleted: %s", ns_uuid
)
268 def ns_instance_list(self
):
269 self
._log
.debug("Getting NS instance list")
272 output_lines
= self
._openmano
_cmd
(
273 ["instance-scenario-list"],
276 except OpenmanoCommandFailed
as e
:
277 self
._log
.warning("Instance scenario listing returned an error: %s", str(e
))
280 if "No scenario instances were found" in output_lines
[0]:
281 self
._log
.debug("No openmano instances were found")
285 for line
in output_lines
:
287 uuid
, name
= line
.split(" ", 1)
288 name_uuid_map
[name
.strip()] = uuid
.strip()
290 self
._log
.debug("Instance Scenario list: {}".format(name_uuid_map
))
293 def ns_instance_scenario_create(self
, instance_yaml_str
):
294 """ Create a Openmano NS instance from input YAML string """
296 self
._log
.debug("Instantiating instance: %s", instance_yaml_str
)
298 with tempfile
.NamedTemporaryFile() as ns_instance_file_hdl
:
299 ns_instance_file_hdl
.write(instance_yaml_str
.encode())
300 ns_instance_file_hdl
.flush()
303 output_lines
= self
._openmano
_cmd
(
304 ["instance-scenario-create", ns_instance_file_hdl
.name
],
307 except OpenmanoCommandFailed
as e
:
310 uuid
, _
= output_lines
[0].split(" ", 1)
312 self
._log
.info("NS Instance Created: %s", uuid
)
317 def ns_vim_network_create(self
, net_create_yaml_str
,datacenter_name
):
318 """ Create a Openmano VIM network from input YAML string """
320 self
._log
.debug("Creating VIM network instance: %s, DC %s", net_create_yaml_str
,datacenter_name
)
322 with tempfile
.NamedTemporaryFile() as net_create_file_hdl
:
323 net_create_file_hdl
.write(net_create_yaml_str
.encode())
324 net_create_file_hdl
.flush()
327 output_lines
= self
._openmano
_cmd
(
328 ["vim-net-create","--datacenter", datacenter_name
, net_create_file_hdl
.name
],
331 except OpenmanoCommandFailed
as e
:
334 uuid
, _
= output_lines
[0].split(" ", 1)
336 self
._log
.info("VIM Networks created in DC %s with ID: %s", datacenter_name
, uuid
)
340 def ns_vim_network_delete(self
, network_name
,datacenter_name
):
341 """ Delete a Openmano VIM network with given name """
343 self
._log
.debug("Deleting VIM network instance: %s, DC %s", network_name
,datacenter_name
)
345 output_lines
= self
._openmano
_cmd
(
346 ["vim-net-delete","--datacenter", datacenter_name
, network_name
],
349 except OpenmanoCommandFailed
as e
:
351 self
._log
.info("VIM Network deleted in DC %s with name: %s", datacenter_name
, network_name
)
354 def ns_instantiate(self
, scenario_name
, instance_name
, datacenter_name
=None):
356 "Instantiating NS %s using instance name %s",
361 cmd_args
= ["scenario-deploy", scenario_name
, instance_name
]
362 if datacenter_name
is not None:
363 cmd_args
.extend(["--datacenter", datacenter_name
])
365 output_lines
= self
._openmano
_cmd
(
370 uuid
, _
= output_lines
[0].split(" ", 1)
372 self
._log
.info("NS Instance Created: %s", uuid
)
376 def ns_terminate(self
, ns_instance_name
):
377 self
._log
.info("Terminating NS: %s", ns_instance_name
)
380 ["instance-scenario-delete", ns_instance_name
, "-f"],
383 self
._log
.info("NS Instance Deleted: %s", ns_instance_name
)
385 def datacenter_list(self
):
386 lines
= self
._openmano
_cmd
(["datacenter-list",])
388 # The results returned from openmano are formatted with whitespace and
389 # datacenter names may contain whitespace as well, so we use a regular
390 # expression to parse each line of the results return from openmano to
391 # extract the uuid and name of a datacenter.
393 uuid_pattern
= '(xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)'.replace('x', hex)
394 name_pattern
= '(.+?)'
395 datacenter_regex
= re
.compile(r
'{uuid}\s+\b{name}\s*$'.format(
400 # Parse the results for the datacenter uuids and names
403 result
= datacenter_regex
.match(line
)
404 if result
is not None:
405 uuid
, name
= result
.groups()
406 datacenters
.append((uuid
, name
))
411 def valid_uuid(uuid_str
):
412 uuid_re
= re
.compile(
413 "^xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx$".replace('x', '[0-9a-fA-F]')
416 if not uuid_re
.match(uuid_str
):
417 raise argparse
.ArgumentTypeError("Got a valid uuid: %s" % uuid_str
)
422 def parse_args(argv
=sys
.argv
[1:]):
423 """ Parse the command line arguments
426 argv - The list of arguments to parse
429 Argparse Namespace instance
431 parser
= argparse
.ArgumentParser()
435 help="Openmano host/ip",
441 help="Openmano port",
448 help="Openmano tenant uuid to use",
451 subparsers
= parser
.add_subparsers(dest
='command', help='openmano commands')
453 vnf_create_parser
= subparsers
.add_parser(
455 help="Adds a openmano vnf into the catalog"
457 vnf_create_parser
.add_argument(
459 help="location of the JSON file describing the VNF",
460 type=argparse
.FileType('rb'),
463 vnf_delete_parser
= subparsers
.add_parser(
465 help="Deletes a openmano vnf into the catalog"
467 vnf_delete_parser
.add_argument(
469 help="The vnf to delete",
473 _
= subparsers
.add_parser(
475 help="List all the openmano VNFs in the catalog",
478 ns_create_parser
= subparsers
.add_parser(
480 help="Adds a openmano ns scenario into the catalog"
482 ns_create_parser
.add_argument(
484 help="location of the JSON file describing the NS",
485 type=argparse
.FileType('rb'),
488 ns_delete_parser
= subparsers
.add_parser(
490 help="Deletes a openmano ns into the catalog"
492 ns_delete_parser
.add_argument(
494 help="The ns to delete",
498 _
= subparsers
.add_parser(
500 help="List all the openmano scenarios in the catalog",
503 ns_instance_create_parser
= subparsers
.add_parser(
505 help="Deploys a openmano ns scenario into the catalog"
507 ns_instance_create_parser
.add_argument(
509 help="The ns scenario name to deploy",
511 ns_instance_create_parser
.add_argument(
513 help="The ns instance name to deploy",
517 ns_instance_delete_parser
= subparsers
.add_parser(
518 'instance-scenario-delete',
519 help="Deploys a openmano ns scenario into the catalog"
521 ns_instance_delete_parser
.add_argument(
523 help="The ns instance name to delete",
527 _
= subparsers
.add_parser(
528 'instance-scenario-list',
529 help="List all the openmano scenario instances in the catalog",
532 _
= subparsers
.add_parser(
534 help="List all the openmano datacenters",
537 args
= parser
.parse_args(argv
)
543 logging
.basicConfig(level
=logging
.DEBUG
)
544 logger
= logging
.getLogger("openmano_client.py")
546 if "RIFT_INSTALL" not in os
.environ
:
547 logger
.error("Must be in rift-shell to run.")
551 openmano_cli
= OpenmanoCliAPI(logger
, args
.host
, args
.port
, args
.tenant
)
553 if args
.command
== "vnf-create":
554 openmano_cli
.vnf_create(args
.file.read())
556 elif args
.command
== "vnf-delete":
557 openmano_cli
.vnf_delete(args
.uuid
)
559 elif args
.command
== "vnf-list":
560 for uuid
, name
in openmano_cli
.vnf_list().items():
561 print("{} {}".format(uuid
, name
))
563 elif args
.command
== "scenario-create":
564 openmano_cli
.ns_create(args
.file.read())
566 elif args
.command
== "scenario-delete":
567 openmano_cli
.ns_delete(args
.uuid
)
569 elif args
.command
== "scenario-list":
570 for uuid
, name
in openmano_cli
.ns_list().items():
571 print("{} {}".format(uuid
, name
))
573 elif args
.command
== "scenario-deploy":
574 openmano_cli
.ns_instantiate(args
.scenario_name
, args
.instance_name
)
576 elif args
.command
== "instance-scenario-delete":
577 openmano_cli
.ns_terminate(args
.instance_name
)
579 elif args
.command
== "instance-scenario-list":
580 for uuid
, name
in openmano_cli
.ns_instance_list().items():
581 print("{} {}".format(uuid
, name
))
583 elif args
.command
== "datacenter-list":
584 for uuid
, name
in openmano_cli
.datacenter_list():
585 print("{} {}".format(uuid
, name
))
588 logger
.error("Unknown command: %s", args
.command
)
591 if __name__
== "__main__":