diff options
author | Lingxian Kong <anlin.kong@gmail.com> | 2020-01-16 23:57:18 +1300 |
---|---|---|
committer | Lingxian Kong <anlin.kong@gmail.com> | 2020-01-17 17:21:26 +1300 |
commit | 095a5b0514f800258a3e3cccc94c94135cab8ac5 (patch) | |
tree | e11d8b79f8f634fa23daacfcd476bd8bea852ef2 | |
parent | baf8fee0a165de39c63d0ae0f72de7222a6f420c (diff) | |
download | trove-095a5b0514f800258a3e3cccc94c94135cab8ac5.tar.gz |
Check network conflict
The user's network to create instance should not conflict with the
management network.
This patch also fixes the bug in trove-guestagent:
AttributeError: 'TroveContext' object has no attribute 'notification'
which has been merged in master branch in
https://review.opendev.org/#/c/697225/
Change-Id: I922a1c5469309704cc6dd60a1ef57e43a98a3c00
(cherry picked from commit 263339b4db0fb8e1d10c9933b71a0d5b7140cc68)
-rw-r--r-- | .zuul.yaml | 2 | ||||
-rwxr-xr-x | integration/scripts/trovestack | 5 | ||||
-rw-r--r-- | trove/common/exception.py | 4 | ||||
-rw-r--r-- | trove/common/neutron.py | 25 | ||||
-rw-r--r-- | trove/instance/models.py | 5 | ||||
-rw-r--r-- | trove/instance/service.py | 33 |
6 files changed, 61 insertions, 13 deletions
@@ -15,7 +15,6 @@ - openstack-tox-pylint - trove-tox-bandit-baseline: voting: false - - trove-tox-fakemodetests - trove-functional-mysql - trove-scenario-mysql-single - trove-scenario-mysql-multi @@ -26,7 +25,6 @@ queue: trove jobs: - openstack-tox-pylint - - trove-tox-fakemodetests - trove-functional-mysql - trove-scenario-mysql-single - trove-scenario-mysql-multi diff --git a/integration/scripts/trovestack b/integration/scripts/trovestack index c24647fe..654ef6f8 100755 --- a/integration/scripts/trovestack +++ b/integration/scripts/trovestack @@ -552,9 +552,7 @@ function cmd_set_datastore() { PACKAGES=${PACKAGES:-""} if [ "$DATASTORE_TYPE" == "mysql" ]; then - set_mysql_pkg - PACKAGES=${PACKAGES:-$MYSQL_PKG} - VERSION=$MYSQL_VER + VERSION="5.7" elif [ "$DATASTORE_TYPE" == "percona" ]; then PACKAGES=${PACKAGES:-"percona-server-server-5.6"} VERSION="5.6" @@ -562,7 +560,6 @@ function cmd_set_datastore() { PACKAGES=${PACKAGES:-"percona-xtradb-cluster-server-5.6"} VERSION="5.6" elif [ "$DATASTORE_TYPE" == "mariadb" ]; then - PACKAGES=${PACKAGES:-"mariadb-server"} VERSION="10.4" elif [ "$DATASTORE_TYPE" == "mongodb" ]; then PACKAGES=${PACKAGES:-"mongodb-org"} diff --git a/trove/common/exception.py b/trove/common/exception.py index e6cc91e2..a03f2bee 100644 --- a/trove/common/exception.py +++ b/trove/common/exception.py @@ -596,6 +596,10 @@ class PublicNetworkNotFound(TroveError): message = _("Public network cannot be found.") +class NetworkConflict(BadRequest): + message = _("User network conflicts with the management network.") + + class ClusterVolumeSizeRequired(TroveError): message = _("A volume size is required for each instance in the cluster.") diff --git a/trove/common/neutron.py b/trove/common/neutron.py index 5f3330b1..d11ecf22 100644 --- a/trove/common/neutron.py +++ b/trove/common/neutron.py @@ -21,6 +21,7 @@ from trove.common import exception CONF = cfg.CONF LOG = logging.getLogger(__name__) MGMT_NETWORKS = None +MGMT_CIDRS = None def get_management_networks(context): @@ -147,3 +148,27 @@ def create_security_group_rule(client, sg_id, protocol, ports, remote_ips): } client.create_security_group_rule(body) + + +def get_subnet_cidrs(client, network_id): + cidrs = [] + + subnets = client.list_subnets(network_id=network_id)['subnets'] + for subnet in subnets: + cidrs.append(subnet.get('cidr')) + + return cidrs + + +def get_mamangement_subnet_cidrs(client): + """Cache the management subnet CIDRS.""" + global MGMT_CIDRS + + if MGMT_CIDRS is not None: + return MGMT_CIDRS + + MGMT_CIDRS = [] + if len(CONF.management_networks) > 0: + MGMT_CIDRS = get_subnet_cidrs(client, CONF.management_networks[0]) + + return MGMT_CIDRS diff --git a/trove/instance/models.py b/trove/instance/models.py index 356fcc7d..7a13af2e 100644 --- a/trove/instance/models.py +++ b/trove/instance/models.py @@ -24,6 +24,7 @@ import six from novaclient import exceptions as nova_exceptions from oslo_config.cfg import NoSuchOptError from oslo_log import log as logging +from oslo_utils import encodeutils from sqlalchemy import func from trove.backup.models import Backup @@ -1811,6 +1812,10 @@ class instance_encryption_key_cache(object): if val is None: return val + # We need string anyway + if isinstance(val, six.binary_type): + val = encodeutils.safe_decode(val) + if len(self._lru) == self._lru_cache_size: tail = self._lru.pop() del self._table[tail] diff --git a/trove/instance/service.py b/trove/instance/service.py index ce339704..05e433ea 100644 --- a/trove/instance/service.py +++ b/trove/instance/service.py @@ -13,6 +13,8 @@ # License for the specific language governing permissions and limitations # under the License. +import ipaddress + from oslo_log import log as logging from oslo_utils import strutils @@ -20,9 +22,10 @@ from trove.backup.models import Backup as backup_model from trove.backup import views as backup_views import trove.common.apischema as apischema from trove.common import cfg -from trove.common.clients import create_guest_client +from trove.common import clients from trove.common import exception from trove.common.i18n import _ +from trove.common import neutron from trove.common import notification from trove.common.notification import StartNotification from trove.common import pagination @@ -278,6 +281,19 @@ class InstanceController(wsgi.Controller): instance.delete() return wsgi.Result(None, 202) + def _check_network_overlap(self, context, user_network): + neutron_client = clients.create_neutron_client(context) + user_cidrs = neutron.get_subnet_cidrs(neutron_client, user_network) + mgmt_cidrs = neutron.get_mamangement_subnet_cidrs(neutron_client) + LOG.debug("Cidrs of the user network: %s, cidrs of the management " + "network: %s", user_cidrs, mgmt_cidrs) + for user_cidr in user_cidrs: + user_net = ipaddress.ip_network(user_cidr) + for mgmt_cidr in mgmt_cidrs: + mgmt_net = ipaddress.ip_network(mgmt_cidr) + if user_net.overlaps(mgmt_net): + raise exception.NetworkConflict() + def create(self, req, body, tenant_id): # TODO(hub-cap): turn this into middleware LOG.info("Creating a database instance for tenant '%s'", @@ -341,7 +357,10 @@ class InstanceController(wsgi.Controller): backup_id = None availability_zone = body['instance'].get('availability_zone') + nics = body['instance'].get('nics', []) + if len(nics) > 0: + self._check_network_overlap(context, nics[0].get('net-id')) slave_of_id = body['instance'].get('replica_of', # also check for older name @@ -498,7 +517,7 @@ class InstanceController(wsgi.Controller): if not instance: raise exception.NotFound(uuid=id) self.authorize_instance_action(context, 'guest_log_list', instance) - client = create_guest_client(context, id) + client = clients.create_guest_client(context, id) guest_log_list = client.guest_log_list() return wsgi.Result({'logs': guest_log_list}, 200) @@ -522,7 +541,7 @@ class InstanceController(wsgi.Controller): discard = body.get('discard', None) if enable and disable: raise exception.BadRequest(_("Cannot enable and disable log.")) - client = create_guest_client(context, id) + client = clients.create_guest_client(context, id) guest_log = client.guest_log_action(log_name, enable, disable, publish, discard) return wsgi.Result({'log': guest_log}, 200) @@ -545,13 +564,13 @@ class InstanceController(wsgi.Controller): def _module_list_guest(self, context, id, include_contents): """Return information about modules on an instance.""" - client = create_guest_client(context, id) + client = clients.create_guest_client(context, id) result_list = client.module_list(include_contents) return wsgi.Result({'modules': result_list}, 200) def _module_list(self, context, id, include_contents): """Return information about instance modules.""" - client = create_guest_client(context, id) + client = clients.create_guest_client(context, id) result_list = client.module_list(include_contents) return wsgi.Result({'modules': result_list}, 200) @@ -567,7 +586,7 @@ class InstanceController(wsgi.Controller): module_models.Modules.validate( modules, instance.datastore.id, instance.datastore_version.id) module_list = module_views.convert_modules_to_list(modules) - client = create_guest_client(context, id) + client = clients.create_guest_client(context, id) result_list = client.module_apply(module_list) models.Instance.add_instance_modules(context, id, modules) return wsgi.Result({'modules': result_list}, 200) @@ -581,7 +600,7 @@ class InstanceController(wsgi.Controller): self.authorize_instance_action(context, 'module_remove', instance) module = module_models.Module.load(context, module_id) module_info = module_views.DetailedModuleView(module).data() - client = create_guest_client(context, id) + client = clients.create_guest_client(context, id) client.module_remove(module_info) instance_modules = module_models.InstanceModules.load_all( context, instance_id=id, module_id=module_id) |