python-troveclient/troveclient/osc/v1/database_instances.py

728 lines
25 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.
"""Database v1 Instances action implementations"""
import argparse
import six
from osc_lib.command import command
from osc_lib import exceptions
from osc_lib import utils as osc_utils
from oslo_utils import uuidutils
from troveclient.i18n import _
from troveclient.osc.v1 import base
from troveclient import utils as trove_utils
def set_attributes_for_print(instances):
for instance in instances:
# To avoid invoking GET request to trove.
instance_info = instance.to_dict()
setattr(instance, 'flavor_id', instance.flavor['id'])
if 'volume' in instance_info:
setattr(instance, 'size', instance.volume['size'])
else:
setattr(instance, 'size', '-')
setattr(instance, 'role', '')
if 'replica_of' in instance_info:
setattr(instance, 'role', 'replica')
if 'replicas' in instance_info:
setattr(instance, 'role', 'primary')
if 'datastore' in instance_info:
if instance.datastore.get('version'):
setattr(instance, 'datastore_version',
instance.datastore['version'])
setattr(instance, 'datastore', instance.datastore['type'])
return instances
def set_attributes_for_print_detail(instance):
info = instance._info.copy()
info['flavor'] = instance.flavor['id']
if hasattr(instance, 'volume'):
info['volume'] = instance.volume['size']
if 'used' in instance.volume:
info['volume_used'] = instance.volume['used']
if hasattr(instance, 'ip'):
info['ip'] = ', '.join(instance.ip)
if hasattr(instance, 'datastore'):
info['datastore'] = instance.datastore['type']
info['datastore_version'] = instance.datastore['version']
if hasattr(instance, 'configuration'):
info['configuration'] = instance.configuration['id']
if hasattr(instance, 'replica_of'):
info['replica_of'] = instance.replica_of['id']
if hasattr(instance, 'replicas'):
replicas = [replica['id'] for replica in instance.replicas]
info['replicas'] = ', '.join(replicas)
if hasattr(instance, 'networks'):
info['networks'] = instance.networks['name']
info['networks_id'] = instance.networks['id']
if hasattr(instance, 'fault'):
info.pop('fault', None)
info['fault'] = instance.fault['message']
info['fault_date'] = instance.fault['created']
if 'details' in instance.fault and instance.fault['details']:
info['fault_details'] = instance.fault['details']
info.pop('links', None)
return info
class ListDatabaseInstances(command.Lister):
_description = _("List database instances")
columns = ['ID', 'Name', 'Datastore', 'Datastore Version', 'Status',
'Addresses', 'Flavor ID', 'Size', 'Region', 'Role']
admin_columns = [
'ID', 'Name', 'Tenant ID', 'Datastore', 'Datastore Version', 'Status',
'Addresses', 'Flavor ID', 'Size', 'Role'
]
def get_parser(self, prog_name):
parser = super(ListDatabaseInstances, self).get_parser(prog_name)
parser.add_argument(
'--limit',
dest='limit',
metavar='<limit>',
default=None,
help=_('Limit the number of results displayed.')
)
parser.add_argument(
'--marker',
dest='marker',
metavar='<ID>',
type=str,
default=None,
help=_('Begin displaying the results for IDs greater than the'
'specified marker. When used with ``--limit``, set '
'this to the last ID displayed in the previous run.')
)
parser.add_argument(
'--include_clustered', '--include-clustered',
dest='include_clustered',
action="store_true",
default=False,
help=_("Include instances that are part of a cluster "
"(default %(default)s). --include-clustered may be "
"deprecated in the future, retaining just "
"--include_clustered.")
)
parser.add_argument(
'--all-projects',
dest='all_projects',
action="store_true",
default=False,
help=_("Include database instances of all projects (admin only)")
)
return parser
def take_action(self, parsed_args):
if parsed_args.all_projects:
db_instances = self.app.client_manager.database.mgmt_instances
cols = self.admin_columns
else:
db_instances = self.app.client_manager.database.instances
cols = self.columns
instances = db_instances.list(
limit=parsed_args.limit,
marker=parsed_args.marker,
include_clustered=parsed_args.include_clustered
)
if instances:
instances = set_attributes_for_print(instances)
instances = [osc_utils.get_item_properties(i, cols)
for i in instances]
return cols, instances
class ShowDatabaseInstance(command.ShowOne):
_description = _("Show instance details")
def get_parser(self, prog_name):
parser = super(ShowDatabaseInstance, self).get_parser(prog_name)
parser.add_argument(
'instance',
metavar='<instance>',
help=_('Instance (name or ID)'),
)
return parser
def take_action(self, parsed_args):
db_instances = self.app.client_manager.database.instances
instance = osc_utils.find_resource(db_instances, parsed_args.instance)
instance = set_attributes_for_print_detail(instance)
return zip(*sorted(six.iteritems(instance)))
class DeleteDatabaseInstance(base.TroveDeleter):
_description = _("Deletes an instance.")
def get_parser(self, prog_name):
parser = super(DeleteDatabaseInstance, self).get_parser(prog_name)
parser.add_argument(
'instance',
nargs='+',
metavar='instance',
help='Id or name of instance(s).'
)
parser.add_argument(
'--force',
action="store_true",
default=False,
help=_('Force delete the instance, will reset the instance status '
'before deleting.'),
)
return parser
def take_action(self, parsed_args):
db_instances = self.app.client_manager.database.instances
# Used for batch deletion
self.delete_func = (db_instances.force_delete if parsed_args.force
else db_instances.delete)
self.resource = 'database instance'
ids = []
for instance_id in parsed_args.instance:
if not uuidutils.is_uuid_like(instance_id):
try:
instance_id = trove_utils.get_resource_id_by_name(
db_instances, instance_id
)
except Exception as e:
msg = ("Failed to get database instance %s, error: %s" %
(instance_id, str(e)))
raise exceptions.CommandError(msg)
ids.append(instance_id)
self.delete_resources(ids)
class CreateDatabaseInstance(command.ShowOne):
_description = _("Creates a new database instance.")
def get_parser(self, prog_name):
parser = super(CreateDatabaseInstance, self).get_parser(prog_name)
parser.add_argument(
'name',
metavar='<name>',
help=_("Name of the instance."),
)
parser.add_argument(
'--flavor',
metavar='<flavor>',
type=str,
help=_("A flavor ID."),
)
parser.add_argument(
'--size',
metavar='<size>',
type=int,
default=None,
help=_("Size of the instance disk volume in GB. "
"Required when volume support is enabled."),
)
parser.add_argument(
'--volume-type',
metavar='<volume_type>',
type=str,
default=None,
help=_("Volume type. Optional when volume support is enabled."),
)
parser.add_argument(
'--databases',
metavar='<database>',
nargs="+",
default=[],
help=_("Optional list of databases."),
)
parser.add_argument(
'--users',
metavar='<user:password>',
nargs="+",
default=[],
help=_("Optional list of users."),
)
parser.add_argument(
'--backup',
metavar='<backup>',
default=None,
help=_("A backup name or ID."),
)
parser.add_argument(
'--availability-zone',
metavar='<availability_zone>',
default=None,
help=_("The Zone hint to give to Nova."),
)
parser.add_argument(
'--datastore',
metavar='<datastore>',
default=None,
help=_("A datastore name or ID."),
)
parser.add_argument(
'--datastore-version',
metavar='<datastore_version>',
default=None,
help=_("A datastore version name or ID."),
)
parser.add_argument(
'--nic',
metavar='<net-id=<net-uuid>>',
dest='nics',
help=_("Create instance in the given Neutron network."),
)
parser.add_argument(
'--configuration',
metavar='<configuration>',
default=None,
help=_("ID of the configuration group to attach to the instance."),
)
parser.add_argument(
'--replica-of',
metavar='<source_instance>',
default=None,
help=_("ID or name of an existing instance to replicate from."),
)
parser.add_argument(
'--replica-count',
metavar='<count>',
type=int,
default=None,
help=_("Number of replicas to create (defaults to 1 if "
"replica_of specified)."),
)
parser.add_argument(
'--module',
metavar='<module>',
type=str,
dest='modules',
action='append',
default=[],
help=_("ID or name of the module to apply. Specify multiple "
"times to apply multiple modules."),
)
parser.add_argument(
'--locality',
metavar='<policy>',
default=None,
choices=['affinity', 'anti-affinity'],
help=_("Locality policy to use when creating replicas. Choose "
"one of %(choices)s."),
)
parser.add_argument(
'--region',
metavar='<region>',
type=str,
default=None,
help=argparse.SUPPRESS,
)
parser.add_argument(
'--is-public',
action='store_true',
help="Whether or not to make the instance public.",
)
parser.add_argument(
'--allowed-cidr',
action='append',
dest='allowed_cidrs',
help="The IP CIDRs that are allowed to access the database "
"instance.",
)
return parser
def take_action(self, parsed_args):
database = self.app.client_manager.database
db_instances = database.instances
volume = None
if parsed_args.size is not None and parsed_args.size <= 0:
raise exceptions.ValidationError(
_("Volume size '%s' must be an integer and greater than 0.")
% parsed_args.size)
elif parsed_args.size:
volume = {"size": parsed_args.size,
"type": parsed_args.volume_type}
restore_point = None
if parsed_args.backup:
restore_point = {"backupRef": osc_utils.find_resource(
database.backups, parsed_args.backup).id}
replica_of = None
replica_count = parsed_args.replica_count
if parsed_args.replica_of:
replica_of = osc_utils.find_resource(
db_instances, parsed_args.replica_of)
replica_count = replica_count or 1
locality = None
if parsed_args.locality:
locality = parsed_args.locality
if replica_of:
raise exceptions.ValidationError(
_('Cannot specify locality when adding replicas '
'to existing master.'))
databases = [{'name': value} for value in parsed_args.databases]
users = [{'name': n, 'password': p, 'databases': databases} for (n, p)
in
[z.split(':')[:2] for z in parsed_args.users]]
nics = []
if parsed_args.nics:
nic_info = dict(
[(k, v) for (k, v) in [parsed_args.nics.split("=", 1)[:2]]]
)
if not nic_info.get('net-id'):
raise exceptions.ValidationError(
"net-id is not set in %s" % parsed_args.nics
)
nics.append(nic_info)
modules = []
for module in parsed_args.modules:
modules.append(osc_utils.find_resource(database.modules,
module).id)
access = {'is_public': False}
if parsed_args.is_public:
access['is_public'] = True
if parsed_args.allowed_cidrs:
access['allowed_cidrs'] = parsed_args.allowed_cidrs
instance = db_instances.create(
parsed_args.name,
flavor_id=parsed_args.flavor,
volume=volume,
databases=databases,
users=users,
restorePoint=restore_point,
availability_zone=(parsed_args.availability_zone),
datastore=parsed_args.datastore,
datastore_version=(parsed_args.datastore_version),
nics=nics,
configuration=parsed_args.configuration,
replica_of=replica_of,
replica_count=replica_count,
modules=modules,
locality=locality,
region_name=parsed_args.region,
access=access
)
instance = set_attributes_for_print_detail(instance)
return zip(*sorted(six.iteritems(instance)))
class ResetDatabaseInstanceStatus(command.Command):
_description = _("Set instance service status to ERROR and clear the "
"current task status. Mark any running backup operations "
"as FAILED.")
def get_parser(self, prog_name):
parser = super(ResetDatabaseInstanceStatus, self).get_parser(prog_name)
parser.add_argument(
'instance',
metavar='<instance>',
help=_('ID or name of the instance'),
)
return parser
def take_action(self, parsed_args):
db_instances = self.app.client_manager.database.instances
instance = osc_utils.find_resource(db_instances,
parsed_args.instance)
db_instances.reset_status(instance)
class ResizeDatabaseInstanceFlavor(command.Command):
_description = _("Resize an instance with a new flavor")
def get_parser(self, prog_name):
parser = super(ResizeDatabaseInstanceFlavor, self).get_parser(
prog_name
)
parser.add_argument(
'instance',
metavar='<instance>',
type=str,
help=_('ID or name of the instance')
)
parser.add_argument(
'flavor_id',
metavar='<flavor_id>',
type=str,
help=_('New flavor ID of the instance')
)
return parser
def take_action(self, parsed_args):
instance_mgr = self.app.client_manager.database.instances
instance_id = parsed_args.instance
if not uuidutils.is_uuid_like(instance_id):
instance_id = osc_utils.find_resource(instance_mgr, instance_id)
instance_mgr.resize_instance(instance_id, parsed_args.flavor_id)
class UpgradeDatabaseInstance(command.Command):
_description = _("Upgrades an instance to a new datastore version.")
def get_parser(self, prog_name):
parser = super(UpgradeDatabaseInstance, self).get_parser(prog_name)
parser.add_argument(
'instance',
metavar='<instance>',
type=str,
help=_('ID or name of the instance.'),
)
parser.add_argument(
'datastore_version',
metavar='<datastore_version>',
help=_('ID or name of the instance.'),
)
return parser
def take_action(self, parsed_args):
db_instances = self.app.client_manager.database.instances
instance = osc_utils.find_resource(db_instances,
parsed_args.instance)
db_instances.upgrade(instance, parsed_args.datastore_version)
class ResizeDatabaseInstanceVolume(command.Command):
_description = _("Resizes the volume size of an instance.")
def get_parser(self, prog_name):
parser = super(ResizeDatabaseInstanceVolume, self).get_parser(
prog_name
)
parser.add_argument(
'instance',
metavar='<instance>',
type=str,
help=_('ID or name of the instance.')
)
parser.add_argument(
'size',
metavar='<size>',
type=int,
default=None,
help=_('New size of the instance disk volume in GB.')
)
return parser
def take_action(self, parsed_args):
db_instances = self.app.client_manager.database.instances
instance = osc_utils.find_resource(db_instances,
parsed_args.instance)
db_instances.resize_volume(instance, parsed_args.size)
class ForceDeleteDatabaseInstance(command.Command):
_description = _("Force delete an instance.")
def get_parser(self, prog_name):
parser = (
super(ForceDeleteDatabaseInstance, self).get_parser(prog_name))
parser.add_argument(
'instance',
metavar='<instance>',
help=_('ID or name of the instance'),
)
return parser
def take_action(self, parsed_args):
db_instances = self.app.client_manager.database.instances
instance = osc_utils.find_resource(db_instances,
parsed_args.instance)
db_instances.reset_status(instance)
try:
db_instances.delete(instance)
except Exception as e:
msg = (_("Failed to delete instance %(instance)s: %(e)s")
% {'instance': parsed_args.instance, 'e': e})
raise exceptions.CommandError(msg)
class PromoteDatabaseInstanceToReplicaSource(command.Command):
_description = _(
"Promotes a replica to be the new replica source of its set.")
def get_parser(self, prog_name):
parser = super(PromoteDatabaseInstanceToReplicaSource,
self).get_parser(prog_name)
parser.add_argument(
'instance',
metavar='<instance>',
type=str,
help=_('ID or name of the instance.'),
)
return parser
def take_action(self, parsed_args):
db_instances = self.app.client_manager.database.instances
instance = osc_utils.find_resource(db_instances,
parsed_args.instance)
db_instances.promote_to_replica_source(instance)
class RestartDatabaseInstance(command.Command):
_description = _("Restarts an instance.")
def get_parser(self, prog_name):
parser = super(RestartDatabaseInstance, self).get_parser(
prog_name
)
parser.add_argument(
'instance',
metavar='<instance>',
type=str,
help=_('ID or name of the instance.')
)
return parser
def take_action(self, parsed_args):
db_instances = self.app.client_manager.database.instances
instance = osc_utils.find_resource(db_instances,
parsed_args.instance)
db_instances.restart(instance)
class EjectDatabaseInstanceReplicaSource(command.Command):
_description = _("Ejects a replica source from its set.")
def get_parser(self, prog_name):
parser = super(EjectDatabaseInstanceReplicaSource, self).get_parser(
prog_name)
parser.add_argument(
'instance',
metavar='<instance>',
type=str,
help=_('ID or name of the instance.'),
)
return parser
def take_action(self, parsed_args):
db_instances = self.app.client_manager.database.instances
instance = osc_utils.find_resource(db_instances,
parsed_args.instance)
db_instances.eject_replica_source(instance)
class UpdateDatabaseInstance(command.Command):
_description = _("Updates an instance: Edits name, "
"configuration, or replica source.")
def get_parser(self, prog_name):
parser = super(UpdateDatabaseInstance, self).get_parser(prog_name)
parser.add_argument(
'instance',
metavar='<instance>',
type=str,
help=_('ID or name of the instance.'),
)
parser.add_argument(
'--name',
metavar='<name>',
type=str,
default=None,
help=_('ID or name of the instance.'),
)
parser.add_argument(
'--configuration',
metavar='<configuration>',
type=str,
default=None,
help=_('ID of the configuration reference to attach.'),
)
parser.add_argument(
'--detach_replica_source',
'--detach-replica-source',
dest='detach_replica_source',
action="store_true",
default=False,
help=_('Detach the replica instance from its replication source. '
'--detach-replica-source may be deprecated in the future '
'in favor of just --detach_replica_source'),
)
parser.add_argument(
'--remove_configuration',
dest='remove_configuration',
action="store_true",
default=False,
help=_('Drops the current configuration reference.'),
)
return parser
def take_action(self, parsed_args):
db_instances = self.app.client_manager.database.instances
instance = osc_utils.find_resource(db_instances,
parsed_args.instance)
db_instances.edit(instance, parsed_args.configuration,
parsed_args.name,
parsed_args.detach_replica_source,
parsed_args.remove_configuration)
class DetachDatabaseInstanceReplica(command.Command):
_description = _("Detaches a replica instance "
"from its replication source.")
def get_parser(self, prog_name):
parser = super(DetachDatabaseInstanceReplica, self).get_parser(
prog_name)
parser.add_argument(
'instance',
metavar='<instance>',
type=str,
help=_('ID or name of the instance.'),
)
return parser
def take_action(self, parsed_args):
db_instances = self.app.client_manager.database.instances
instance = osc_utils.find_resource(db_instances,
parsed_args.instance)
db_instances.edit(instance, detach_replica_source=True)
class RebootDatabaseInstance(command.Command):
_description = _("Reboots an instance(the Nova server).")
def get_parser(self, prog_name):
parser = super(RebootDatabaseInstance, self).get_parser(prog_name)
parser.add_argument(
'instance',
metavar='<instance>',
type=str,
help=_('ID or name of the instance.'))
return parser
def take_action(self, parsed_args):
instance_id = parsed_args.instance
if not uuidutils.is_uuid_like(instance_id):
instance_mgr = self.app.client_manager.database.instances
instance_id = osc_utils.find_resource(instance_mgr, instance_id)
mgmt_instance_mgr = self.app.client_manager.database.mgmt_instances
mgmt_instance_mgr.reboot(instance_id)