diff --git a/devstack/plugin.sh b/devstack/plugin.sh index e785d89c07..a85de23779 100755 --- a/devstack/plugin.sh +++ b/devstack/plugin.sh @@ -935,14 +935,27 @@ function download_image { } function import_docker_service_image_ubuntu { - GZIPPED_IMG_NAME=`basename "$MANILA_DOCKER_IMAGE_URL"` - IMG_NAME_LOAD=${GZIPPED_IMG_NAME%.*} - LOCAL_IMG_NAME=${IMG_NAME_LOAD%.*} - if [[ "$(sudo docker images -q $LOCAL_IMG_NAME)" == "" ]]; then - download_image $MANILA_DOCKER_IMAGE_URL - # Import image in Docker - gzip -d $FILES/$GZIPPED_IMG_NAME - sudo docker load --input $FILES/$IMG_NAME_LOAD + if [[ $MANILA_DOCKER_IMAGE_URL == *"/"*":"* ]] || [[ $MANILA_DOCKER_IMAGE_URL == *"/"*"/"* ]]; then + # Registry reference (e.g., quay.io/org/image:tag) + if [[ "$(sudo docker images -q $MANILA_DOCKER_IMAGE_URL)" == "" ]]; then + sudo docker pull "$MANILA_DOCKER_IMAGE_URL" + fi + sudo docker tag "$MANILA_DOCKER_IMAGE_URL" manila-docker-container + else + # Tarball URL (http/https/file) + GZIPPED_IMG_NAME=$(basename "$MANILA_DOCKER_IMAGE_URL") + IMG_NAME_LOAD=${GZIPPED_IMG_NAME%.*} + LOCAL_IMG_NAME=${IMG_NAME_LOAD%.*} + if [[ "$(sudo docker images -q $LOCAL_IMG_NAME)" == "" ]]; then + download_image $MANILA_DOCKER_IMAGE_URL + if [[ $MANILA_DOCKER_IMAGE_URL == file* ]]; then + local src_path + src_path=$(echo "$MANILA_DOCKER_IMAGE_URL" | sed "s|^file://||") + cp "$src_path" "$FILES/$GZIPPED_IMG_NAME" + fi + gzip -d "$FILES/$GZIPPED_IMG_NAME" + sudo docker load --input "$FILES/$IMG_NAME_LOAD" + fi fi } @@ -1035,6 +1048,19 @@ function setup_ipv6 { # Enabling interface is needed due to NEUTRON_CREATE_INITIAL_NETWORKS=False sudo ip link set $PUBLIC_BRIDGE up + # Allocate a separate IPv6 address on the public network for FRR's BGP + # identity so that FRR and os-ken (neutron BGP speaker) have distinct + # addresses - required for BGP peering on a single-host devstack. + local frr_bgp_ipv6=$(openstack --os-cloud devstack-admin port create \ + frr-bgp-peer --network $PUBLIC_NETWORK_NAME \ + --fixed-ip subnet=$IPV6_PUBLIC_SUBNET_NAME \ + -c fixed_ips -f value | grep -oE '[0-9a-f]+:[:0-9a-f]+') + sudo ip -6 addr add "$frr_bgp_ipv6"/$SUBNETPOOL_SIZE_V6 dev $PUBLIC_BRIDGE + + # Diagnostic: show kernel source address selection for connections to FRR + echo "=== Source address for connections to FRR ($frr_bgp_ipv6) ===" + ip -6 route get $frr_bgp_ipv6 + if [ "$SHARE_DRIVER" == "manila.share.drivers.lvm.LVMShareDriver" ]; then for backend_name in ${MANILA_ENABLED_BACKENDS//,/ }; do iniset $MANILA_CONF $backend_name lvm_share_export_ips $public_gateway_ipv4,$public_gateway_ipv6 @@ -1049,10 +1075,14 @@ function setup_ipv6 { iniset $MANILA_CONF DEFAULT data_node_access_ips $public_gateway_ipv4 fi - # install Quagga for setting up the host routes dynamically - install_package quagga + # install FRR for setting up the host routes dynamically + install_package frr - # set Quagga daemons + # Remove the default integrated config so FRR reads per-daemon + # config files (bgpd.conf, zebra.conf) in traditional mode. + sudo rm -f /etc/frr/frr.conf + + # set FRR daemons ( echo "zebra=yes" echo "bgpd=yes" @@ -1062,61 +1092,67 @@ function setup_ipv6 { echo "ripngd=no" echo "isisd=no" echo "babeld=no" - ) | sudo tee /etc/quagga/daemons > /dev/null + # Explicitly listen on all addresses (IPv4+IPv6); Debian's default + # daemons file restricts bgpd to 127.0.0.1 which blocks IPv6 peering. + echo 'zebra_options=" -A 127.0.0.1 -s 90000000"' + echo 'bgpd_options=" -A ::"' + ) | sudo tee /etc/frr/daemons > /dev/null - # set Quagga zebra.conf + # set FRR zebra.conf ( echo "hostname dsvm" echo "password openstack" - echo "log file /var/log/quagga/zebra.log" - ) | sudo tee /etc/quagga/zebra.conf > /dev/null + echo "log syslog informational" + echo "log file $DEST/logs/frr/zebra.log" + ) | sudo tee /etc/frr/zebra.conf > /dev/null - # set Quagga vtysh.conf + # set FRR vtysh.conf ( echo "service integrated-vtysh-config" - echo "username quagga nopassword" - ) | sudo tee /etc/quagga/vtysh.conf > /dev/null - - # set Quagga bgpd.conf + ) | sudo tee /etc/frr/vtysh.conf > /dev/null + + # set FRR bgpd.conf + # Use "bgp listen range" with a peer-group instead of a static neighbor + # so FRR accepts BGP connections from any address in the public subnet. + # This avoids source-address-matching issues on single-host devstack + # where os-ken and FRR share the same network namespace. + local public_subnet_cidr=$(openstack --os-cloud devstack-admin \ + subnet show $IPV6_PUBLIC_SUBNET_NAME -c cidr -f value) ( - echo "log file /var/log/quagga/bgpd.log" - echo "bgp multiple-instance" + echo "log syslog informational" + echo "log file $DEST/logs/frr/bgpd.log" echo "router bgp 200" echo " bgp router-id 1.2.3.4" - echo " neighbor $public_gateway_ipv6 remote-as 100" - echo " neighbor $public_gateway_ipv6 passive" - echo " address-family ipv6" - echo " neighbor $public_gateway_ipv6 activate" - echo "line vty" + echo " no bgp ebgp-requires-policy" + echo " timers bgp 10 30" + echo " neighbor OSKEN peer-group" + echo " neighbor OSKEN remote-as 100" + echo " neighbor OSKEN timers 10 30" + echo " bgp listen range $public_subnet_cidr peer-group OSKEN" + echo " address-family ipv6 unicast" + echo " neighbor OSKEN activate" + echo " exit-address-family" + echo "!" echo "debug bgp events" echo "debug bgp filters" echo "debug bgp fsm" echo "debug bgp keepalives" echo "debug bgp updates" - ) | sudo tee /etc/quagga/bgpd.conf > /dev/null - - # Quagga logging - sudo mkdir -p /var/log/quagga - sudo touch /var/log/quagga/zebra.log - sudo touch /var/log/quagga/bgpd.log - sudo chown -R quagga:quagga /var/log/quagga + ) | sudo tee /etc/frr/bgpd.conf > /dev/null + # FRR logging — write directly under $DEST/logs so Zuul collects them + sudo mkdir -p $DEST/logs/frr + sudo chown frr:frr $DEST/logs/frr - GetOSVersion - QUAGGA_SERVICES="zebra bgpd" - if [[ is_ubuntu && "$os_CODENAME" == "xenial" ]]; then - # In Ubuntu Xenial, the services bgpd and zebra are under - # one systemd unit: quagga - QUAGGA_SERVICES="quagga" - elif is_fedora; then - # Disable SELinux rule that conflicts with Zebra - sudo setsebool -P zebra_write_config 1 - fi - sudo systemctl enable $QUAGGA_SERVICES - sudo systemctl restart $QUAGGA_SERVICES + sudo systemctl enable frr + sudo systemctl restart frr - # log the systemd status - sudo systemctl status $QUAGGA_SERVICES + # log the systemd status and BGP diagnostics + sudo systemctl status frr + sleep 2 + sudo ss -tlnp '( sport = 179 )' + sudo vtysh -c "show bgp summary" + sudo cat /etc/frr/bgpd.conf # This will fail with mutltiple default routes and is not needed in CI # but may be useful when developing with devstack locally @@ -1132,10 +1168,12 @@ function setup_ipv6 { } function setup_bgp_for_ipv6 { - public_gateway_ipv6=$(openstack --os-cloud devstack-admin subnet show ipv6-public-subnet -c gateway_ip -f value) + # FRR's separate BGP identity address (allocated in setup_ipv6) + local frr_bgp_ipv6=$(openstack --os-cloud devstack-admin port show \ + frr-bgp-peer -c fixed_ips -f value | grep -oE '[0-9a-f]+:[:0-9a-f]+') openstack --os-cloud devstack-admin bgp speaker create --ip-version 6 --local-as 100 bgpspeaker openstack --os-cloud devstack-admin bgp speaker add network bgpspeaker $PUBLIC_NETWORK_NAME - openstack --os-cloud devstack-admin bgp peer create --peer-ip $public_gateway_ipv6 --remote-as 200 bgppeer + openstack --os-cloud devstack-admin bgp peer create --peer-ip $frr_bgp_ipv6 --remote-as 200 bgppeer openstack --os-cloud devstack-admin bgp speaker add peer bgpspeaker bgppeer } diff --git a/devstack/settings b/devstack/settings index 6a8e74a414..b7dbdb72fe 100644 --- a/devstack/settings +++ b/devstack/settings @@ -167,9 +167,7 @@ MANILA_ZFSONLINUX_REPLICATION_DOMAIN=${MANILA_ZFSONLINUX_REPLICATION_DOMAIN:-"ZF MANILA_CONTAINER_DRIVER=${MANILA_CONTAINER_DRIVER:-"manila.share.drivers.container.driver.ContainerShareDriver"} MANILA_DOCKER_IMAGE_ALIAS=${MANILA_DOCKER_IMAGE_ALIAS:-"manila_docker_image"} MANILA_CONTAINER_VOLUME_GROUP_NAME=${MANILA_CONTAINER_VOLUME_GROUP_NAME:-"manila_docker_volumes"} -# (aovchinnikov): This location is temporary and will be changed to a -# permanent one as soon as possible. -MANILA_DOCKER_IMAGE_URL=${MANILA_DOCKER_IMAGE_URL:-"https://github.com/a-ovchinnikov/manila-image-elements-lxd-images/releases/download/0.1.0/manila-docker-container.tar.gz"} +MANILA_DOCKER_IMAGE_URL=${MANILA_DOCKER_IMAGE_URL:-"quay.io/openstack-manila/manila-docker-container:latest"} # Network Plugin MANILA_NETWORK_API_CLASS=${MANILA_NETWORK_API_CLASS:-"manila.network.neutron.neutron_network_plugin.NeutronBindNetworkPlugin"} diff --git a/manila/db/sqlalchemy/api.py b/manila/db/sqlalchemy/api.py index af0d64c119..342e14bf60 100644 --- a/manila/db/sqlalchemy/api.py +++ b/manila/db/sqlalchemy/api.py @@ -6607,10 +6607,16 @@ def _availability_zone_create_if_not_exist(context, name): try: return _availability_zone_get(context, name) except exception.AvailabilityZoneNotFound: - az = models.AvailabilityZone() - az.update({'id': uuidutils.generate_uuid(), 'name': name}) - az.save(context.session) - return az + try: + nested = context.session.begin_nested() + az = models.AvailabilityZone() + az.update({'id': uuidutils.generate_uuid(), 'name': name}) + az.save(context.session) + nested.commit() + return az + except db_exception.DBDuplicateEntry: + nested.rollback() + return _availability_zone_get(context, name) @require_context @@ -8009,6 +8015,8 @@ def resource_lock_get_all(context, filters=None, limit=None, offset=None, all_projects = filters.get('all_projects') or filters.get('all_tenants') if project_id is None and not all_projects: filters['project_id'] = context.project_id + if project_id is not None: + authorize_project_context(context, project_id) legal_filter_keys = ('id', 'user_id', 'resource_id', 'resource_type', 'lock_context', 'resource_action', 'created_since', diff --git a/manila/scheduler/drivers/filter.py b/manila/scheduler/drivers/filter.py index afdaa1ef29..0d059bbac6 100644 --- a/manila/scheduler/drivers/filter.py +++ b/manila/scheduler/drivers/filter.py @@ -340,7 +340,7 @@ def _log_share_error(self, share_id, retry): "%(last_host)s : %(exc)s", { "share_id": share_id, "last_host": last_host, - "exc": "exc" + "exc": exc }) def _populate_scheduler_hint(self, request_spec, hints, key, hint): diff --git a/manila/share/manager.py b/manila/share/manager.py index 73c1a3f151..b550df03d0 100644 --- a/manila/share/manager.py +++ b/manila/share/manager.py @@ -5653,7 +5653,7 @@ def create_backup_continue(self, context): backup_id) except Exception: LOG.warning("Failed to get progress of share %(share)s " - "backing up in share_backup %(backup).", + "backing up in share_backup %(backup)s.", {'share': share_id, 'backup': backup_id}) self.db.share_update( context, share_id, @@ -5790,7 +5790,7 @@ def restore_backup_continue(self, context): backup_id) except Exception: LOG.exception("Failed to get progress of share_backup " - "%(backup)s restoring in share %(share).", + "%(backup)s restoring in share %(share)s.", {'share': share_id, 'backup': backup_id}) self.db.share_update( context, share_id, diff --git a/manila/tests/db/sqlalchemy/test_api.py b/manila/tests/db/sqlalchemy/test_api.py index da63a26c15..616445a1c7 100644 --- a/manila/tests/db/sqlalchemy/test_api.py +++ b/manila/tests/db/sqlalchemy/test_api.py @@ -4349,6 +4349,52 @@ def test_az_get_all(self): self.assertEqual(1, len(actual_result)) self.assertEqual('test2', actual_result[0]['name']) + def test_az_create_if_not_exist_handles_duplicate(self): + """Concurrent services can race to create the same AZ.""" + az_name = 'test_race_az' + # Create the AZ first. + with db_api.context_manager.writer.using(self.ctxt): + existing_az = db_api._availability_zone_create_if_not_exist( + self.ctxt, az_name, + ) + # Call again — this should just return the existing AZ + # rather than failing with DBDuplicateEntry. + with db_api.context_manager.writer.using(self.ctxt): + result = db_api._availability_zone_create_if_not_exist( + self.ctxt, az_name, + ) + self.assertEqual(existing_az['id'], result['id']) + self.assertEqual(az_name, result['name']) + + def test_az_create_if_not_exist_race_condition(self): + """Simulate race: GET misses, INSERT conflicts, retry GET works.""" + az_name = 'test_race_az' + # Pre-create the AZ so the INSERT will conflict. + with db_api.context_manager.writer.using(self.ctxt): + existing_az = db_api._availability_zone_create_if_not_exist( + self.ctxt, az_name, + ) + # Mock _availability_zone_get to miss on first call (simulating + # the race window) but succeed on the retry after DBDuplicateEntry. + original_get = db_api._availability_zone_get + call_count = [0] + + def side_effect(ctx, name): + call_count[0] += 1 + if call_count[0] == 1: + raise exception.AvailabilityZoneNotFound(id=name) + return original_get(ctx, name) + + with mock.patch.object( + db_api, '_availability_zone_get', side_effect=side_effect, + ): + with db_api.context_manager.writer.using(self.ctxt): + result = db_api._availability_zone_create_if_not_exist( + self.ctxt, az_name, + ) + self.assertEqual(existing_az['id'], result['id']) + self.assertEqual(2, call_count[0]) + @ddt.ddt class NetworkAllocationsDatabaseAPITestCase(test.TestCase): diff --git a/releasenotes/notes/bug-2008813-fix-az-creation-logic-38f1198480b54fd1.yaml b/releasenotes/notes/bug-2008813-fix-az-creation-logic-38f1198480b54fd1.yaml new file mode 100644 index 0000000000..3c7247fdb4 --- /dev/null +++ b/releasenotes/notes/bug-2008813-fix-az-creation-logic-38f1198480b54fd1.yaml @@ -0,0 +1,6 @@ +--- +fixes: + - | + A `bug previously caused `_ + failures during simultaneous service startup due to an incorrect + database rollback. This has now been addressed. diff --git a/releasenotes/notes/bug-2161287-prevent-unprivileged-locks-filtering-9f40a2f48475429e.yaml b/releasenotes/notes/bug-2161287-prevent-unprivileged-locks-filtering-9f40a2f48475429e.yaml new file mode 100644 index 0000000000..c3f318e8fb --- /dev/null +++ b/releasenotes/notes/bug-2161287-prevent-unprivileged-locks-filtering-9f40a2f48475429e.yaml @@ -0,0 +1,7 @@ +--- +security: + - | + Manila now prevents resource locks to be filtered by a project UUID in + case the requester is not an admininistrator or a member of the project + they are attempting to look up. For more details, please refer to + `Launchpad bug `.