python-muranoclient/muranoclient/osc/v1/environment.py

488 lines
16 KiB
Python

# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""Application-catalog v1 stack action implementation"""
import json
import six
import sys
import uuid
import jsonpatch
from osc_lib.command import command
from osc_lib import utils
from oslo_log import log as logging
from oslo_serialization import jsonutils
from muranoclient.apiclient import exceptions
from muranoclient.common import utils as murano_utils
LOG = logging.getLogger(__name__)
class ListEnvironments(command.Lister):
"""Lists environments"""
def get_parser(self, prog_name):
parser = super(ListEnvironments, self).get_parser(prog_name)
parser.add_argument(
'--all-tenants',
action='store_true',
default=False,
help='List environments from all tenants (admin only).',
)
return parser
def take_action(self, parsed_args):
LOG.debug("take_action({0})".format(parsed_args))
client = self.app.client_manager.application_catalog
data = client.environments.list(parsed_args.all_tenants)
columns = ('id', 'name', 'status', 'created', 'updated')
column_headers = [c.capitalize() for c in columns]
return (
column_headers,
list(utils.get_item_properties(
s,
columns,
) for s in data)
)
class ShowEnvironment(command.ShowOne):
"""Display environment details"""
def get_parser(self, prog_name):
parser = super(ShowEnvironment, self).get_parser(prog_name)
parser.add_argument(
"id",
metavar="<NAME or ID>",
help=("Name or ID of the environment to display"),
)
parser.add_argument(
"--only-apps",
action='store_true',
default=False,
help="Only print apps of the environment (useful for automation).",
)
parser.add_argument(
"--session-id",
metavar="<SESSION_ID>",
default='',
help="Id of a config session.",
)
return parser
def take_action(self, parsed_args):
LOG.debug("take_action({0})".format(parsed_args))
client = self.app.client_manager.application_catalog
environment = utils.find_resource(client.environments,
parsed_args.id)
data = client.environments.get(environment.id,
parsed_args.session_id).to_dict()
data['services'] = jsonutils.dumps(data['services'], indent=2)
if getattr(parsed_args, 'only_apps', False):
return(['services'], [data['services']])
else:
return self.dict2columns(data)
class RenameEnvironment(command.Lister):
"""Rename an environment."""
def get_parser(self, prog_name):
parser = super(RenameEnvironment, self).get_parser(prog_name)
parser.add_argument(
'id',
metavar="<NAME or ID>",
help="Environment ID or name.",
)
parser.add_argument(
'name',
metavar="<ENVIRONMENT_NAME>",
help="A name to which the environment will be renamed.",
)
return parser
def take_action(self, parsed_args):
LOG.debug("take_action({0})".format(parsed_args))
client = self.app.client_manager.application_catalog
environment = utils.find_resource(client.environments,
parsed_args.id)
data = client.environments.update(environment.id,
parsed_args.name)
columns = ('id', 'name', 'status', 'created', 'updated')
column_headers = [c.capitalize() for c in columns]
return (
column_headers,
[utils.get_item_properties(
data,
columns,
)]
)
class EnvironmentSessionCreate(command.ShowOne):
"""Creates a new configuration session for environment ID."""
def get_parser(self, prog_name):
parser = super(EnvironmentSessionCreate, self).get_parser(prog_name)
parser.add_argument(
'id',
metavar="<ID>",
help="ID of Environment to add session to.",
)
return parser
def take_action(self, parsed_args):
LOG.debug("take_action({0})".format(parsed_args))
client = self.app.client_manager.application_catalog
environment_id = parsed_args.id
session_id = client.sessions.configure(environment_id).id
sessionid = murano_utils.text_wrap_formatter(session_id)
return (['id'], [sessionid])
class EnvironmentCreate(command.Lister):
"""Create an environment."""
def get_parser(self, prog_name):
parser = super(EnvironmentCreate, self).get_parser(prog_name)
parser.add_argument(
'name',
metavar="<ENVIRONMENT_ID>",
help="Environment name.",
)
parser.add_argument(
'--region',
metavar="<REGION_NAME>",
help="Name of the target OpenStack region.",
)
parser.add_argument(
'--join-subnet-id',
metavar="<SUBNET_ID>",
help="Subnetwork id to join.",
)
parser.add_argument(
'--join-net-id',
metavar="<NET_ID>",
help="Network id to join.",
)
return parser
def take_action(self, parsed_args):
LOG.debug("take_action({0})".format(parsed_args))
client = self.app.client_manager.application_catalog
body = {"name": parsed_args.name, "region": parsed_args.region}
if parsed_args.join_net_id or parsed_args.join_subnet_id:
res = {
'defaultNetworks': {
'environment': {
'?': {
'id': uuid.uuid4().hex,
'type':
'io.murano.resources.ExistingNeutronNetwork'
},
},
'flat': None
}
}
if parsed_args.join_net_id:
res['defaultNetworks']['environment']['internalNetworkName'] \
= parsed_args.join_net_id
if parsed_args.join_subnet_id:
res['defaultNetworks']['environment']['internalSubnetworkName'
] = \
parsed_args.join_subnet_id
body.update(res)
data = client.environments.create(body)
columns = ('id', 'name', 'status', 'created', 'updated')
column_headers = [c.capitalize() for c in columns]
return (
column_headers,
[utils.get_item_properties(
data,
columns,
)]
)
class EnvironmentDelete(command.Lister):
"""Delete an environment."""
def get_parser(self, prog_name):
parser = super(EnvironmentDelete, self).get_parser(prog_name)
parser.add_argument(
'id',
metavar="<NAME or ID>",
nargs="+",
help="Id or name of environment(s) to delete.",
)
parser.add_argument(
'--abandon',
action='store_true',
default=False,
help="If set will abandon environment without deleting any of its"
" resources.",
)
return parser
def take_action(self, parsed_args):
LOG.debug("take_action({0})".format(parsed_args))
client = self.app.client_manager.application_catalog
abandon = getattr(parsed_args, 'abandon', False)
failure_count = 0
for environment_id in parsed_args.id:
try:
environment = murano_utils.find_resource(client.environments,
environment_id)
client.environments.delete(environment.id, abandon)
except exceptions.NotFound:
failure_count += 1
print("Failed to delete '{0}'; environment not found".
format(environment_id))
if failure_count == len(parsed_args.id):
raise exceptions.CommandError("Unable to find and delete any of "
"the specified environments.")
data = client.environments.list()
columns = ('id', 'name', 'status', 'created', 'updated')
column_headers = [c.capitalize() for c in columns]
return (
column_headers,
list(utils.get_item_properties(
s,
columns,
) for s in data)
)
class EnvironmentDeploy(command.ShowOne):
"""Start deployment of a murano environment session."""
def get_parser(self, prog_name):
parser = super(EnvironmentDeploy, self).get_parser(prog_name)
parser.add_argument(
'id',
metavar="<ENVIRONMENT_ID>",
help="ID of Environment to deploy.",
)
parser.add_argument(
'--session-id',
metavar="<SESSION>",
help="ID of configuration session to deploy.",
)
return parser
def take_action(self, parsed_args):
LOG.debug("take_action({0})".format(parsed_args))
client = self.app.client_manager.application_catalog
client.sessions.deploy(parsed_args.id, parsed_args.session_id)
environment = utils.find_resource(client.environments,
parsed_args.id)
data = client.environments.get(environment.id,
parsed_args.session_id).to_dict()
data['services'] = jsonutils.dumps(data['services'], indent=2)
if getattr(parsed_args, 'only_apps', False):
return(['services'], [data['services']])
else:
return self.dict2columns(data)
class EnvironmentAppsEdit(command.Command):
"""Edit environment's services list.
`FILE` is path to a file, that contains jsonpatch, that describes changes
to be made to environment's object-model.
[
{ "op": "add", "path": "/-",
"value": { ... your-app object model here ... }
},
{ "op": "replace", "path": "/0/?/name",
"value": "new_name"
},
]
NOTE: Values '===id1===', '===id2===', etc. in the resulting object-model
will be substituted with uuids.
For more info on jsonpatch see RFC 6902
"""
def get_parser(self, prog_name):
parser = super(EnvironmentAppsEdit, self).get_parser(prog_name)
parser.add_argument(
'id',
metavar="<ENVIRONMENT_ID>",
help="ID of Environment to edit.",
)
parser.add_argument(
'filename',
metavar="<FILE>",
help="File to read jsonpatch from (defaults to stdin).",
)
parser.add_argument(
'--session-id',
metavar="<SESSION>",
help="ID of configuration session to edit.",
)
return parser
def take_action(self, parsed_args):
LOG.debug("take_action(%s)", parsed_args)
client = self.app.client_manager.application_catalog
jp_obj = None
if not parsed_args.filename:
jp_obj = json.load(sys.stdin)
else:
with open(parsed_args.filename) as fpatch:
jp_obj = json.load(fpatch)
jpatch = jsonpatch.JsonPatch(jp_obj)
environment_id = parsed_args.id
session_id = parsed_args.session_id
environment = client.environments.get(environment_id, session_id)
object_model = jpatch.apply(environment.services)
murano_utils.traverse_and_replace(object_model)
client.services.put(
environment_id,
path='/',
data=jpatch.apply(environment.services),
session_id=session_id)
class EnvironmentModelShow(command.ShowOne):
"""Show environment's object model."""
def get_parser(self, prog_name):
parser = super(EnvironmentModelShow, self).get_parser(prog_name)
parser.add_argument(
'id',
metavar="<ENVIRONMENT_ID>",
help="ID of Environment to show.",
)
parser.add_argument(
"--path",
metavar="<PATH>",
default='/',
help="Path to Environment model section. Defaults to '/'."
)
parser.add_argument(
'--session-id',
metavar="<SESSION_ID>",
help="Id of a config session.",
)
return parser
def take_action(self, parsed_args):
LOG.debug("take_action(%s)", parsed_args)
client = self.app.client_manager.application_catalog
session_id = parsed_args.session_id or None
path = six.moves.urllib.parse.quote(parsed_args.path)
env_model = client.environments.get_model(parsed_args.id, path,
session_id)
return self.dict2columns(env_model)
class EnvironmentModelEdit(command.ShowOne):
"""Edit environment's object model."""
def get_parser(self, prog_name):
parser = super(EnvironmentModelEdit, self).get_parser(prog_name)
parser.add_argument(
'id',
metavar="<ENVIRONMENT_ID>",
help="ID of Environment to edit.",
)
parser.add_argument(
"filename",
metavar="<FILE>",
nargs="?",
help="File to read JSON-patch from (defaults to stdin)."
)
parser.add_argument(
'--session-id',
metavar="<SESSION_ID>",
help="Id of a config session.",
)
return parser
def take_action(self, parsed_args):
LOG.debug("take_action(%s)", parsed_args)
client = self.app.client_manager.application_catalog
jp_obj = None
if not parsed_args.filename:
jp_obj = json.load(sys.stdin)
else:
with open(parsed_args.filename) as fpatch:
jp_obj = json.load(fpatch)
if not isinstance(jp_obj, list):
raise exceptions.CommandError(
'JSON-patch must be a list of changes')
for change in jp_obj:
if 'op' not in change or 'path' not in change:
raise exceptions.CommandError(
'Every change in JSON-patch must contain "op" and "path" '
'keys')
op = change['op']
if op not in ['add', 'replace', 'remove']:
raise exceptions.CommandError('The value of "op" item must be '
'"add", "replace" or "remove", '
'got {0}'.format(op))
if op != 'remove' and 'value' not in change:
raise exceptions.CommandError('"add" or "replace" change in '
'JSON-patch must contain "value"'
' key')
session_id = parsed_args.session_id
new_model = client.environments.update_model(parsed_args.id, jp_obj,
session_id)
return self.dict2columns(new_model)