src/opera/commands/info.py
import argparse
from os import path
from pathlib import Path, PurePath
from typing import Dict, Optional, Union
import shtab
import yaml
from opera_tosca_parser.error import ParseError as ToscaParserParseError
from opera_tosca_parser.parser import tosca
from opera.error import DataError, ParseError
from opera.instance.topology import Topology
from opera.storage import Storage
from opera.utils import format_outputs, save_outputs
def add_parser(subparsers):
parser = subparsers.add_parser(
"info",
help="Show information about the current project"
)
parser.add_argument(
"--instance-path", "-p",
help="Storage folder location (instead of default .opera)"
).complete = shtab.DIR
parser.add_argument(
"--format", "-f", choices=("yaml", "json"), type=str, default="yaml",
help="Output format",
)
parser.add_argument(
"--output", "-o",
help="Output file location"
)
parser.add_argument(
"--verbose", "-v", action="store_true",
help="Turns on verbose mode",
)
parser.add_argument(
"csar_or_rootdir", nargs="?",
help="Path to a packed or unpacked TOSCA CSAR (defaults to the current directory if not specified)",
).complete = shtab.DIR
parser.set_defaults(func=_parser_callback)
def _parser_callback(args):
if args.instance_path and not path.isdir(args.instance_path):
raise argparse.ArgumentTypeError(f"Directory {args.instance_path} is not a valid path!")
storage = Storage.create(args.instance_path)
try:
if args.csar_or_rootdir is None:
csar_path = "."
else:
csar_path = args.csar_or_rootdir
outs = info(PurePath(csar_path), storage)
if args.output:
save_outputs(outs, args.format, args.output)
else:
print(format_outputs(outs, args.format).strip())
except ParseError as e:
print(f"{e.loc}: {e}")
return 1
except DataError as e:
print(str(e))
return 1
return 0
def info(csar_or_rootdir: Optional[PurePath], storage: Storage) -> dict: # pylint: disable=too-many-statements
info_dict: Dict[str, Optional[Union[str, dict, bool]]] = dict(
service_template=None,
content_root=None,
inputs=None,
status=None,
csar_metadata=None,
service_template_metadata=None,
csar_valid=None
)
# stateless autodetect first if possible,
# which can then be overwritten via state
if csar_or_rootdir is not None:
csar = tosca.load_csar(csar_or_rootdir, False)
try:
# this validates CSAR and the entrypoint's (if it exists) metadata
# failure to validate here means no static information will be available at all
csar.validate_csar()
info_dict["csar_valid"] = True
info_dict["content_root"] = str(csar_or_rootdir)
if csar.get_entrypoint() is not None:
info_dict["service_template"] = str(csar.get_entrypoint())
try:
service_template_meta = csar.parse_service_template_meta(csar.get_entrypoint())
if service_template_meta:
info_dict["service_template_metadata"] = service_template_meta.to_dict()
except ToscaParserParseError:
pass
try:
csar_meta = csar.parse_csar_meta()
if csar_meta:
info_dict["csar_metadata"] = csar_meta.to_dict()
except ToscaParserParseError:
pass
except ToscaParserParseError:
# anything that fails because of validation can be ignored, we can use state
# we mark the CSAR as invalid because it's useful to know
info_dict["csar_valid"] = False
# detection from state, overrides stateless
if storage.exists("root_file"):
service_template_path = PurePath(storage.read("root_file"))
info_dict["service_template"] = str(service_template_path)
if storage.exists("inputs"):
inputs = yaml.safe_load(storage.read("inputs"))
else:
inputs = {}
info_dict["inputs"] = inputs
if storage.exists("csars/csar"):
csar_dir = Path(storage.path) / "csars" / "csar"
info_dict["content_root"] = str(csar_dir)
ast = tosca.load_service_template(Path(csar_dir), service_template_path.relative_to(csar_dir))
try:
csar = tosca.load_csar(csar_dir)
info_dict["csar_valid"] = True
try:
service_template_meta = csar.parse_service_template_meta(csar.get_entrypoint())
if service_template_meta:
info_dict["service_template_metadata"] = service_template_meta.to_dict()
except ToscaParserParseError:
pass
try:
csar_meta = csar.parse_csar_meta()
if csar_meta:
info_dict["csar_metadata"] = csar_meta.to_dict()
except ToscaParserParseError:
pass
except ToscaParserParseError:
info_dict["csar_valid"] = False
else:
ast = tosca.load_service_template(Path(service_template_path.parent), PurePath(service_template_path.name))
if storage.exists("instances"):
template = ast.get_template(inputs)
# We need to instantiate the template in order
# to get access to the instance state.
topology = Topology.instantiate(template, storage)
info_dict["status"] = topology.status()
else:
info_dict["status"] = "initialized"
return info_dict