diff options
-rw-r--r-- | .zuul.yaml | 2 | ||||
-rw-r--r-- | devstack/plugin.sh | 18 | ||||
-rw-r--r-- | doc/source/user/manage-db-and-users.rst | 17 | ||||
-rwxr-xr-x | integration/scripts/files/elements/guest-agent/install.d/guest-agent-source-install/31-guest-agent-install | 3 | ||||
-rw-r--r-- | integration/scripts/files/elements/guest-agent/install.d/guest-agent-source-install/guest-agent.logrotate | 7 | ||||
-rw-r--r-- | releasenotes/source/index.rst | 1 | ||||
-rw-r--r-- | releasenotes/source/xena.rst | 6 | ||||
-rw-r--r-- | trove/common/cfg.py | 4 | ||||
-rw-r--r-- | trove/guestagent/datastore/manager.py | 4 | ||||
-rw-r--r-- | trove/guestagent/strategies/replication/mariadb_gtid.py | 9 | ||||
-rw-r--r-- | trove/guestagent/strategies/replication/mysql_base.py | 8 | ||||
-rw-r--r-- | trove/guestagent/strategies/replication/mysql_gtid.py | 8 | ||||
-rw-r--r-- | trove/guestagent/strategies/replication/postgresql.py | 10 | ||||
-rw-r--r-- | trove/instance/models.py | 40 | ||||
-rwxr-xr-x | trove/taskmanager/models.py | 40 | ||||
-rw-r--r-- | trove/tests/unittests/taskmanager/test_models.py | 44 |
16 files changed, 141 insertions, 80 deletions
@@ -13,7 +13,7 @@ - check-requirements - openstack-cover-jobs # - openstack-lower-constraints-jobs - - openstack-python3-xena-jobs + - openstack-python3-yoga-jobs - periodic-stable-jobs - publish-openstack-docs-pti - release-notes-jobs-python3 diff --git a/devstack/plugin.sh b/devstack/plugin.sh index 2c8b3b7a..01800907 100644 --- a/devstack/plugin.sh +++ b/devstack/plugin.sh @@ -479,8 +479,7 @@ function create_guest_image { fi echo "Add the image to glance" - glance_image_id=$(openstack --os-region-name RegionOne --os-password ${SERVICE_PASSWORD} \ - --os-project-name service --os-username trove \ + glance_image_id=$(openstack --os-cloud trove \ image create ${image_name} \ --disk-format qcow2 --container-format bare \ --tag trove \ @@ -593,16 +592,13 @@ function config_nova_keypair { fi echo "Creating Trove management keypair ${TROVE_MGMT_KEYPAIR_NAME}" - openstack --os-region-name RegionOne --os-password ${SERVICE_PASSWORD} --os-project-name service --os-username trove \ - keypair create --public-key ${SSH_DIR}/id_rsa.pub ${TROVE_MGMT_KEYPAIR_NAME} + openstack --os-cloud trove keypair create --public-key ${SSH_DIR}/id_rsa.pub ${TROVE_MGMT_KEYPAIR_NAME} iniset $TROVE_CONF DEFAULT nova_keypair ${TROVE_MGMT_KEYPAIR_NAME} } function config_cinder_volume_type { - volume_type=$(openstack --os-region-name RegionOne --os-password ${SERVICE_PASSWORD} \ - --os-project-name service --os-username trove \ - volume type list -c Name -f value | awk 'NR==1 {print}') + volume_type=$(openstack --os-cloud trove volume type list -c Name -f value | awk 'NR==1 {print}') iniset $TROVE_CONF DEFAULT cinder_volume_type ${volume_type} } @@ -611,14 +607,12 @@ function config_mgmt_security_group { local sgid echo "Creating Trove management security group." - sgid=$(openstack --os-region-name RegionOne --os-password ${SERVICE_PASSWORD} --os-project-name service --os-username trove security group create ${TROVE_MGMT_SECURITY_GROUP} -f value -c id) + sgid=$(openstack --os-cloud trove security group create ${TROVE_MGMT_SECURITY_GROUP} -f value -c id) # Allow ICMP - openstack --os-region-name RegionOne --os-password ${SERVICE_PASSWORD} --os-project-name service --os-username trove \ - security group rule create --proto icmp $sgid + openstack --os-cloud trove security group rule create --proto icmp $sgid # Allow SSH - openstack --os-region-name RegionOne --os-password ${SERVICE_PASSWORD} --os-project-name service --os-username trove \ - security group rule create --protocol tcp --dst-port 22 $sgid + openstack --os-cloud trove security group rule create --protocol tcp --dst-port 22 $sgid iniset $TROVE_CONF DEFAULT management_security_groups $sgid } diff --git a/doc/source/user/manage-db-and-users.rst b/doc/source/user/manage-db-and-users.rst index ad25b1d8..e6be85d6 100644 --- a/doc/source/user/manage-db-and-users.rst +++ b/doc/source/user/manage-db-and-users.rst @@ -6,12 +6,17 @@ Assume that you installed Trove service and uploaded images with datastore of your choice. This section shows how to manage users and databases in a MySQL 5.7 instance. -.. warning:: +Currently, the Database and User API is only supported by mysql datastore. - Currently, the Database and User API is only supported by mysql datastore. - For others, the recommended way is to get root password (``POST - /v1.0/{project_id}/instances/{instance_id}/root``) and communicate with the - database service directly for database and user management. +For database user management, there are two approaches: + +1. If the ``root_on_create`` option is enabled for the datastore in trove + service config file, the root user password is returned after creating + instance, which can be used directly to access the database. +2. If ``root_on_create=False``, the recommended way is to get root password + (``POST /v1.0/{project_id}/instances/{instance_id}/root`` or ``openstack + database root enable`` in CLI) and communicate with the database service + directly for database and user management. Manage root user ~~~~~~~~~~~~~~~~ @@ -32,7 +37,7 @@ further database operations. If needed, ``openstack database root disable <instance_id>`` command could disable the root user. -Database and User management via Trove API +Database and User management via Trove CLI ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Trove provides API to manage users and databases for mysql datastore. diff --git a/integration/scripts/files/elements/guest-agent/install.d/guest-agent-source-install/31-guest-agent-install b/integration/scripts/files/elements/guest-agent/install.d/guest-agent-source-install/31-guest-agent-install index 0cd63059..cfb6b61e 100755 --- a/integration/scripts/files/elements/guest-agent/install.d/guest-agent-source-install/31-guest-agent-install +++ b/integration/scripts/files/elements/guest-agent/install.d/guest-agent-source-install/31-guest-agent-install @@ -16,7 +16,8 @@ for folder in "/var/lib/trove" "/etc/trove" "/etc/trove/certs" "/etc/trove/conf. chown -R ${GUEST_USERNAME}:root ${folder} done -install -D -g root -o ${GUEST_USERNAME} -m 0644 ${SCRIPTDIR}/guest-agent.logrotate /etc/logrotate.d/guest-agent +# The logrotate file owner should be root or user with uid 0. +install -D -g root -o root -m 0644 ${SCRIPTDIR}/guest-agent.logrotate /etc/logrotate.d/guest-agent # Create a virtual environment (with dependencies installed) for guest agent service /usr/bin/python3 -m venv ${GUEST_VENV} diff --git a/integration/scripts/files/elements/guest-agent/install.d/guest-agent-source-install/guest-agent.logrotate b/integration/scripts/files/elements/guest-agent/install.d/guest-agent-source-install/guest-agent.logrotate index 2a42143a..235b4c48 100644 --- a/integration/scripts/files/elements/guest-agent/install.d/guest-agent-source-install/guest-agent.logrotate +++ b/integration/scripts/files/elements/guest-agent/install.d/guest-agent-source-install/guest-agent.logrotate @@ -1,4 +1,4 @@ -/var/log/guest-agent.log { +/var/log/trove/trove-guestagent.log { daily rotate 10 missingok @@ -6,9 +6,4 @@ compress delaycompress sharedscripts - postrotate - # Signal name shall not have the SIG prefix in kill command - # http://pubs.opengroup.org/onlinepubs/9699919799/utilities/kill.html - kill -s USR1 $(cat /var/run/guest-agent.pid) - endscript } diff --git a/releasenotes/source/index.rst b/releasenotes/source/index.rst index 6143e135..e43d0598 100644 --- a/releasenotes/source/index.rst +++ b/releasenotes/source/index.rst @@ -6,6 +6,7 @@ :maxdepth: 1 unreleased + xena wallaby victoria ussuri diff --git a/releasenotes/source/xena.rst b/releasenotes/source/xena.rst new file mode 100644 index 00000000..1be85be3 --- /dev/null +++ b/releasenotes/source/xena.rst @@ -0,0 +1,6 @@ +========================= +Xena Series Release Notes +========================= + +.. release-notes:: + :branch: stable/xena diff --git a/trove/common/cfg.py b/trove/common/cfg.py index eadb8514..65c030cf 100644 --- a/trove/common/cfg.py +++ b/trove/common/cfg.py @@ -23,9 +23,9 @@ from oslo_config import cfg from oslo_config import types from oslo_config.cfg import NoSuchOptError from oslo_log import log as logging +from oslo_log import versionutils from oslo_middleware import cors from osprofiler import opts as profiler -from oslo_log import versionutils from trove.common.i18n import _ from trove.version import version_info as version @@ -245,7 +245,7 @@ common_opts = [ cfg.IntOpt('trove_conductor_workers', help='Number of workers for the Conductor service. The default ' 'will be the number of CPUs available.'), - cfg.BoolOpt('use_nova_server_config_drive', default=True, + cfg.BoolOpt('use_nova_server_config_drive', default=False, help='Use config drive for file injection when booting ' 'instance.'), cfg.StrOpt('device_path', default='/dev/vdb', diff --git a/trove/guestagent/datastore/manager.py b/trove/guestagent/datastore/manager.py index 097ccc99..442aea21 100644 --- a/trove/guestagent/datastore/manager.py +++ b/trove/guestagent/datastore/manager.py @@ -882,7 +882,7 @@ class Manager(periodic_task.PeriodicTasks): self.replication.enable_as_master(self.app, replica_source_config) LOG.info('Enabled as replication master') - snapshot_id, log_position = self.replication.snapshot_for_replication( + snapshot_id, replica_conf = self.replication.snapshot_for_replication( context, self.app, self.adm, None, snapshot_info) volume_stats = self.get_filesystem_stats(context, None) @@ -896,7 +896,7 @@ class Manager(periodic_task.PeriodicTasks): }, 'replication_strategy': self.replication_strategy, 'master': self.replication.get_master_ref(self.app, snapshot_info), - 'log_position': log_position + 'replica_conf': replica_conf } return replication_snapshot diff --git a/trove/guestagent/strategies/replication/mariadb_gtid.py b/trove/guestagent/strategies/replication/mariadb_gtid.py index 4909ee66..66115806 100644 --- a/trove/guestagent/strategies/replication/mariadb_gtid.py +++ b/trove/guestagent/strategies/replication/mariadb_gtid.py @@ -31,15 +31,16 @@ class MariaDBGTIDReplication(mysql_base.MysqlReplicationBase): master_info = super(MariaDBGTIDReplication, self).get_replica_context( service, adm) + replica_conf = master_info['replica_conf'] get_pos_cmd = 'SELECT @@global.gtid_binlog_pos;' gtid_pos = service.execute_sql(get_pos_cmd).first()[0] LOG.debug('gtid_binlog_pos: %s', gtid_pos) - master_info['log_position']['gtid_pos'] = gtid_pos + replica_conf['log_position']['gtid_pos'] = gtid_pos return master_info def connect_to_master(self, service, master_info): - logging_config = master_info['log_position'] + replica_conf = master_info['replica_conf'] last_gtid = '' if 'dataset' in master_info: @@ -59,8 +60,8 @@ class MariaDBGTIDReplication(mysql_base.MysqlReplicationBase): { 'host': master_info['master']['host'], 'port': master_info['master']['port'], - 'user': logging_config['replication_user']['name'], - 'password': logging_config['replication_user']['password'], + 'user': replica_conf['replication_user']['name'], + 'password': replica_conf['replication_user']['password'] }) service.execute_sql(change_master_cmd) diff --git a/trove/guestagent/strategies/replication/mysql_base.py b/trove/guestagent/strategies/replication/mysql_base.py index 744aec6b..b6e8b6b9 100644 --- a/trove/guestagent/strategies/replication/mysql_base.py +++ b/trove/guestagent/strategies/replication/mysql_base.py @@ -92,10 +92,11 @@ class MysqlReplicationBase(base.Replication): replication_user = self._create_replication_user(service, adm) service.grant_replication_privilege(replication_user) - log_position = { + replica_conf = { + 'log_position': {}, 'replication_user': replication_user } - return snapshot_info['id'], log_position + return snapshot_info['id'], replica_conf def enable_as_master(self, service, master_config): if not service.exists_replication_source_overrides(): @@ -146,7 +147,8 @@ class MysqlReplicationBase(base.Replication): service.grant_replication_privilege(replication_user) return { 'master': self.get_master_ref(service, None), - 'log_position': { + 'replica_conf': { + 'log_position': {}, 'replication_user': replication_user } } diff --git a/trove/guestagent/strategies/replication/mysql_gtid.py b/trove/guestagent/strategies/replication/mysql_gtid.py index f0e8137b..ed0116d1 100644 --- a/trove/guestagent/strategies/replication/mysql_gtid.py +++ b/trove/guestagent/strategies/replication/mysql_gtid.py @@ -38,12 +38,12 @@ class MysqlGTIDReplication(mysql_base.MysqlReplicationBase): set_gtid_cmd = "SET GLOBAL gtid_purged='%s'" % last_gtid service.execute_sql(set_gtid_cmd) - logging_config = master_info['log_position'] + replica_conf = master_info['replica_conf'] LOG.info( "Configure the slave, master: %s:%s, replication user: %s", master_info['master']['host'], master_info['master']['port'], - logging_config['replication_user']['name'] + replica_conf['replication_user']['name'] ) change_master_cmd = ( @@ -57,8 +57,8 @@ class MysqlGTIDReplication(mysql_base.MysqlReplicationBase): { 'host': master_info['master']['host'], 'port': master_info['master']['port'], - 'user': logging_config['replication_user']['name'], - 'password': logging_config['replication_user']['password'] + 'user': replica_conf['replication_user']['name'], + 'password': replica_conf['replication_user']['password'] }) service.execute_sql(change_master_cmd) diff --git a/trove/guestagent/strategies/replication/postgresql.py b/trove/guestagent/strategies/replication/postgresql.py index 5698d5e8..32935512 100644 --- a/trove/guestagent/strategies/replication/postgresql.py +++ b/trove/guestagent/strategies/replication/postgresql.py @@ -136,10 +136,11 @@ class PostgresqlReplicationStreaming(base.Replication): LOG.info('Getting or creating replication user') replication_user = self._get_or_create_replication_user(service) - log_position = { + replica_conf = { + 'log_position': {}, 'replication_user': replication_user } - return snapshot_info['id'], log_position + return snapshot_info['id'], replica_conf def get_master_ref(self, service, snapshot_info): master_ref = { @@ -158,7 +159,7 @@ class PostgresqlReplicationStreaming(base.Replication): as_root=True) LOG.debug("Standby signal file created") - user = snapshot['log_position']['replication_user'] + user = snapshot['replica_conf']['replication_user'] conninfo = (f"host={snapshot['master']['host']} " f"port={snapshot['master']['port']} " f"dbname=postgres " @@ -189,8 +190,9 @@ class PostgresqlReplicationStreaming(base.Replication): repl_user_info = self._get_or_create_replication_user(service) return { + 'log_position': {}, 'master': self.get_master_ref(None, None), - 'log_position': {'replication_user': repl_user_info} + 'replica_conf': {'replication_user': repl_user_info} } def cleanup_source_on_replica_detach(self, admin_service, replica_info): diff --git a/trove/instance/models.py b/trove/instance/models.py index b2659d08..4691b6b9 100644 --- a/trove/instance/models.py +++ b/trove/instance/models.py @@ -15,12 +15,14 @@ # under the License. """Model classes that form the core of instances functionality.""" -from datetime import datetime -from datetime import timedelta +import base64 import json import os.path import re +from datetime import datetime +from datetime import timedelta + from novaclient import exceptions as nova_exceptions from oslo_config.cfg import NoSuchOptError from oslo_log import log as logging @@ -43,8 +45,8 @@ from trove.common.i18n import _ from trove.common.trove_remote import create_trove_client from trove.configuration.models import Configuration from trove.datastore import models as datastore_models -from trove.datastore.models import DatastoreVersionMetadata as dvm from trove.datastore.models import DBDatastoreVersionMetadata +from trove.datastore.models import DatastoreVersionMetadata as dvm from trove.db import get_db_api from trove.db import models as dbmodels from trove.extensions.security_group.models import SecurityGroup @@ -614,9 +616,10 @@ def load_instance(cls, context, id, needs_server=False, def update_service_status(task_status, service_status, ins_id): """Update service status as needed.""" + RESTART_REQUIRED = srvstatus.ServiceStatuses.RESTART_REQUIRED if (task_status == InstanceTasks.NONE and - service_status.status != srvstatus.ServiceStatuses.RESTART_REQUIRED and - not service_status.is_uptodate()): + service_status.status != RESTART_REQUIRED and + not service_status.is_uptodate()): LOG.warning('Guest agent heartbeat for instance %s has expried', ins_id) service_status.status = \ @@ -718,7 +721,8 @@ class BaseInstance(SimpleInstance): from trove.cluster.models import is_cluster_deleting if (self.db_info.cluster_id is not None and not - is_cluster_deleting(self.context, self.db_info.cluster_id)): + is_cluster_deleting(context=self.context, + cluster_id=self.db_info.cluster_id)): raise exception.ClusterInstanceOperationNotSupported() if self.slaves: @@ -963,6 +967,25 @@ class BaseInstance(SimpleInstance): self._server_group_loaded = True return self._server_group + def prepare_cloud_config(self, files): + userdata = ( + "#cloud-config\n" + "write_files:\n" + ) + + for filename, content in files.items(): + ud = encodeutils.safe_encode(content) + body_userdata = ( + "- encoding: b64\n" + " owner: trove:trove\n" + " path: %s\n" + " content: %s\n" % ( + filename, encodeutils.safe_decode(base64.b64encode(ud))) + ) + userdata = userdata + body_userdata + + return userdata + def get_injected_files(self, datastore_manager, datastore_version): injected_config_location = CONF.get('injected_config_location') guest_info = CONF.get('guest_info') @@ -1034,12 +1057,14 @@ class BaseInstance(SimpleInstance): class FreshInstance(BaseInstance): + @classmethod def load(cls, context, id): return load_instance(cls, context, id, needs_server=False) class BuiltInstance(BaseInstance): + @classmethod def load(cls, context, id, needs_server=True): return load_instance(cls, context, id, needs_server=needs_server) @@ -1509,7 +1534,7 @@ class Instance(BuiltInstance): if not self.slaves: raise exception.BadRequest(_("Instance %s is not a replica" - " source.") % self.id) + " source.") % self.id) service = InstanceServiceStatus.find_by(instance_id=self.id) last_heartbeat_delta = timeutils.utcnow() - service.updated_at @@ -1934,6 +1959,7 @@ class DBInstance(dbmodels.DatabaseModelBase): class instance_encryption_key_cache(object): + def __init__(self, func, lru_cache_size=10): self._table = {} self._lru = [] diff --git a/trove/taskmanager/models.py b/trove/taskmanager/models.py index 49742af8..134aecb5 100755 --- a/trove/taskmanager/models.py +++ b/trove/taskmanager/models.py @@ -229,8 +229,8 @@ class ClusterTasks(Cluster): ) def _all_instances_acquire_status( - self, instance_ids, cluster_id, shard_id, expected_status, - fast_fail_statuses=None): + self, instance_ids, cluster_id, shard_id, expected_status, + fast_fail_statuses=None): def _is_fast_fail_status(status): return ((fast_fail_statuses is not None) and @@ -244,7 +244,7 @@ class ClusterTasks(Cluster): task_status = DBInstance.find_by( id=instance_id).get_task_status() if (_is_fast_fail_status(status) or - (task_status == InstanceTasks.BUILDING_ERROR_SERVER)): + (task_status == InstanceTasks.BUILDING_ERROR_SERVER)): # if one has failed, no need to continue polling LOG.debug("Instance %(id)s has acquired a fast-fail " "status %(status)s and" @@ -269,7 +269,7 @@ class ClusterTasks(Cluster): task_status = DBInstance.find_by( id=instance_id).get_task_status() if (_is_fast_fail_status(status) or - (task_status == InstanceTasks.BUILDING_ERROR_SERVER)): + (task_status == InstanceTasks.BUILDING_ERROR_SERVER)): failed_instance_ids.append(instance_id) return failed_instance_ids @@ -373,8 +373,8 @@ class ClusterTasks(Cluster): context.notification = ( DBaaSInstanceUpgrade(context, **request_info)) with StartNotification( - context, instance_id=instance.id, - datastore_version_id=datastore_version.id): + context, instance_id=instance.id, + datastore_version_id=datastore_version.id): with EndNotification(context): instance.update_db( datastore_version_id=datastore_version.id, @@ -781,8 +781,8 @@ class FreshInstanceTasks(FreshInstance, NotifyMixin, ConfigurationMixin): status = service.get_status() if (status == srvstatus.ServiceStatuses.RUNNING or - status == srvstatus.ServiceStatuses.INSTANCE_READY or - status == srvstatus.ServiceStatuses.HEALTHY): + status == srvstatus.ServiceStatuses.INSTANCE_READY or + status == srvstatus.ServiceStatuses.HEALTHY): return True elif status not in [srvstatus.ServiceStatuses.NEW, srvstatus.ServiceStatuses.BUILDING, @@ -986,6 +986,16 @@ class FreshInstanceTasks(FreshInstance, NotifyMixin, ConfigurationMixin): config_drive = CONF.use_nova_server_config_drive key_name = CONF.nova_keypair + # Use config_drive instead by userdata + # We will inject guest config by cloud-config + if not config_drive: + if not userdata: + userdata = self.prepare_cloud_config(files) + else: + userdata = userdata + self.prepare_cloud_config(files) + + files = {} + server = self.nova_client.servers.create( self.name, image_id, flavor_id, key_name=key_name, nics=nics, block_device_mapping_v2=bdmap_v2, @@ -1432,6 +1442,7 @@ class BuiltInstanceTasks(Instance, NotifyMixin, ConfigurationMixin): class BackupTasks(object): + @classmethod def _parse_manifest(cls, manifest): # manifest is in the format 'container/prefix' @@ -1530,11 +1541,11 @@ class ModuleTasks(object): for instance_module in instance_modules: instance_id = instance_module.instance_id if (instance_module.md5 != current_md5 or force) and ( - not md5 or md5 == instance_module.md5): + not md5 or md5 == instance_module.md5): instance = BuiltInstanceTasks.load(context, instance_id, needs_server=False) if instance and ( - include_clustered or not instance.cluster_id): + include_clustered or not instance.cluster_id): try: module_models.Modules.validate( modules, instance.datastore.id, @@ -1550,8 +1561,8 @@ class ModuleTasks(object): # Sleep if we've fired off too many in a row. if (batch_size and - not reapply_count % batch_size and - (reapply_count + skipped_count) < total_count): + not reapply_count % batch_size and + (reapply_count + skipped_count) < total_count): LOG.debug("Applied module to %(cnt)d of %(total)d " "instances - sleeping for %(batch)ds", {'cnt': reapply_count, @@ -1908,7 +1919,7 @@ class ResizeActionBase(object): self._perform_nova_action() finally: if self.instance.db_info.task_status != ( - inst_models.InstanceTasks.NONE): + inst_models.InstanceTasks.NONE): self.instance.reset_task_status() def _guest_is_awake(self): @@ -1988,6 +1999,7 @@ class ResizeActionBase(object): class ResizeAction(ResizeActionBase): + def __init__(self, instance, old_flavor, new_flavor): """ :type instance: trove.taskmanager.models.BuiltInstanceTasks @@ -2043,6 +2055,7 @@ class ResizeAction(ResizeActionBase): class MigrateAction(ResizeActionBase): + def __init__(self, instance, host=None): super(MigrateAction, self).__init__(instance) self.instance = instance @@ -2070,6 +2083,7 @@ class MigrateAction(ResizeActionBase): class RebuildAction(ResizeActionBase): + def __init__(self, instance, image_id): """The action to perform rebuild. diff --git a/trove/tests/unittests/taskmanager/test_models.py b/trove/tests/unittests/taskmanager/test_models.py index fde42e35..5167bc04 100644 --- a/trove/tests/unittests/taskmanager/test_models.py +++ b/trove/tests/unittests/taskmanager/test_models.py @@ -12,43 +12,46 @@ # License for the specific language governing permissions and limitations # under the License. import os + from tempfile import NamedTemporaryFile from unittest import mock -from unittest.mock import call from unittest.mock import MagicMock from unittest.mock import Mock -from unittest.mock import patch from unittest.mock import PropertyMock +from unittest.mock import call +from unittest.mock import patch -from cinderclient import exceptions as cinder_exceptions -from cinderclient.v3 import volumes as cinderclient_volumes import cinderclient.v3.client as cinderclient import neutronclient.v2_0.client as neutronclient -from novaclient import exceptions as nova_exceptions import novaclient.v2.flavors import novaclient.v2.servers + +from cinderclient import exceptions as cinder_exceptions +from cinderclient.v3 import volumes as cinderclient_volumes +from novaclient import exceptions as nova_exceptions from oslo_config import cfg from swiftclient.client import ClientException from testtools.matchers import Equals from testtools.matchers import Is +import trove.backup.models +import trove.common.context +import trove.common.template as template +import trove.db.models +import trove.guestagent.api + from trove import rpc from trove.backup import models as backup_models from trove.backup import state -import trove.backup.models +from trove.common import exception from trove.common import timeutils from trove.common import utils -import trove.common.context -from trove.common import exception from trove.common.exception import GuestError from trove.common.exception import PollTimeOut from trove.common.exception import TroveError -import trove.common.template as template from trove.datastore import models as datastore_models -import trove.db.models from trove.extensions.common import models as common_models from trove.extensions.mysql import models as mysql_models -import trove.guestagent.api from trove.instance.models import BaseInstance from trove.instance.models import DBInstance from trove.instance.models import InstanceServiceStatus @@ -64,6 +67,7 @@ VOLUME_ID = 'volume-id-1' class FakeOptGroup(object): + def __init__(self, tcp_ports=['3306', '3301-3307'], udp_ports=[], icmp=False): self.tcp_ports = tcp_ports @@ -72,6 +76,7 @@ class FakeOptGroup(object): class fake_Server(object): + def __init__(self): self.id = None self.name = None @@ -108,6 +113,7 @@ class fake_ServerManager(object): class fake_nova_client(object): + def __init__(self): self.servers = fake_ServerManager() @@ -236,7 +242,8 @@ class FreshInstanceTasksTest(BaseFreshInstanceTasksTest): server = self.freshinstancetasks._create_server( None, None, datastore_manager, None, None, None) - self.assertEqual(server.userdata, self.userdata) + userdata = self.userdata + "#cloud-config\nwrite_files:\n" + self.assertEqual(server.userdata, userdata) def test_create_instance_with_keypair(self): cfg.CONF.set_override('nova_keypair', 'fake_keypair') @@ -310,7 +317,8 @@ class FreshInstanceTasksTest(BaseFreshInstanceTasksTest): def test_servers_create_block_device_mapping_v2(self, mock_hostname, mock_name): - self.freshinstancetasks.prepare_userdata = Mock(return_value=None) + self.freshinstancetasks.prepare_userdata = Mock( + return_value="#cloud-config\nwrite_files:\n") mock_nova_client = self.freshinstancetasks.nova_client = Mock() mock_servers_create = mock_nova_client.servers.create self.freshinstancetasks._create_server('fake-flavor', 'fake-image', @@ -318,14 +326,18 @@ class FreshInstanceTasksTest(BaseFreshInstanceTasksTest): meta = {'trove_project_id': self.freshinstancetasks.tenant_id, 'trove_user_id': 'test_user', 'trove_instance_id': self.freshinstancetasks.id} + + userdata = self.freshinstancetasks.prepare_userdata('mysql') + userdata = userdata + \ + self.freshinstancetasks.prepare_cloud_config({}) mock_servers_create.assert_called_with( 'fake-name', 'fake-image', 'fake-flavor', files={}, - userdata=None, + userdata=userdata, block_device_mapping_v2=None, availability_zone=None, nics=None, - config_drive=True, + config_drive=False, scheduler_hints=None, key_name=None, meta=meta, @@ -599,6 +611,7 @@ class ResizeVolumeTest(trove_testtools.TestCase): self.new_vol_size) class FakeGroup(object): + def __init__(self): self.mount_point = 'var/lib/mysql' self.device_path = '/dev/vdb' @@ -1105,6 +1118,7 @@ class BackupTasksTest(trove_testtools.TestCase): class NotifyMixinTest(trove_testtools.TestCase): + def test_get_service_id(self): id_map = { 'mysql': '123', |