synchronize default vpc creation across threads

when two clients come for functions where check_default_vpc
is present then one goes to create default vpc and other fails.
it fails because vpc with is_default flag is already in DB
but children objects (like subnets) are not yet created.
For we will lock check_default_vpc function.

Another way is to rework _create_vpc. It can create VPC
without is_default flag. And at the end it can set the flag.
First thread will pass this. And second thread will fail at flag set
operation and will revert all created object. Then second thread
can check presence of default VPC again.
This way is better cause it can work across several controllers.

Change-Id: I5586fa234257b72721e328a9fa2375a56d1553c2
This commit is contained in:
Andrey Pavlov 2018-03-16 16:28:12 +03:00
parent 66a5333f1b
commit 38b882d97d
5 changed files with 30 additions and 7 deletions

View File

@ -215,6 +215,8 @@ function configure_ec2api {
iniset $NOVA_CONF DEFAULT metadata_use_ssl "True"
fi
iniset $EC2API_CONF_FILE oslo_concurrency lock_path $EC2API_STATE_PATH
# configure the database.
iniset $EC2API_CONF_FILE database connection `database_connection_url ec2api`

View File

@ -14,6 +14,7 @@
from neutronclient.common import exceptions as neutron_exception
from oslo_concurrency import lockutils
from oslo_config import cfg
from oslo_log import log as logging
@ -33,6 +34,8 @@ from ec2api.i18n import _
CONF = cfg.CONF
LOG = logging.getLogger(__name__)
synchronized = lockutils.synchronized_with_prefix('ec2api-')
"""VPC-object related API implementation
"""
@ -164,7 +167,13 @@ def _create_vpc(context, cidr_block, is_default=False):
def _check_and_create_default_vpc(context):
if CONF.disable_ec2_classic and not context.is_os_admin:
if not CONF.disable_ec2_classic or context.is_os_admin:
return
lock_name = 'default-vpc-lock-{}-'.format(context.project_id)
@synchronized(lock_name, external=True)
def _check():
for vpc in db_api.get_items(context, 'vpc'):
if vpc.get('is_default'):
return vpc
@ -176,6 +185,8 @@ def _check_and_create_default_vpc(context):
LOG.exception('Failed to create default vpc')
return None
return _check()
ec2utils.set_check_and_create_default_vpc(_check_and_create_default_vpc)

View File

@ -37,7 +37,6 @@ def list_opts():
ec2api.exception.exc_log_opts,
ec2api.paths.path_opts,
ec2api.service.service_opts,
ec2api.utils.utils_opts,
ec2api.wsgi.wsgi_opts,
)),
]

View File

@ -16,9 +16,11 @@ import copy
import itertools
from cinderclient import client as cinderclient
import fixtures
from glanceclient import client as glanceclient
import mock
from novaclient import client as novaclient
from oslo_concurrency import lockutils
from oslo_config import fixture as config_fixture
from oslotest import base as test_base
@ -160,6 +162,11 @@ class BaseTestCase(MockOSMixin, MockDBMixin, test_base.BaseTestCase):
super(BaseTestCase, self).setUp()
self._conf = self.useFixture(config_fixture.Config())
self.configure(fatal_exception_format_errors=True)
lock_path = self.useFixture(fixtures.TempDir()).path
self.fixture = self.useFixture(
config_fixture.Config(lockutils.CONF))
self.fixture.config(lock_path=lock_path,
group='oslo_concurrency')
def configure(self, **kwargs):
self._conf.config(**kwargs)

View File

@ -11,10 +11,10 @@ CONNECTION="mysql://ec2api:ec2api@127.0.0.1/ec2api?charset=utf8"
LOG_DIR=/var/log/ec2api
CONF_DIR=/etc/ec2api
NOVA_CONF=/etc/nova/nova.conf
SIGNING_DIR=/var/cache/ec2api
CONF_FILE=$CONF_DIR/ec2api.conf
APIPASTE_FILE=$CONF_DIR/api-paste.ini
DATA_DIR=${DATA_DIR:-/var/lib/ec2api}
AUTH_CACHE_DIR=${AUTH_CACHE_DIR:-/var/cache/ec2api}
CACHE_BACKEND='oslo_cache.dict'
@ -289,6 +289,8 @@ iniset $CONF_FILE DEFAULT keystone_ec2_tokens_url "$OS_AUTH_URL/v3/ec2tokens"
iniset $CONF_FILE database connection "$CONNECTION"
iniset $CONF_FILE DEFAULT disable_ec2_classic "$DISABLE_EC2_CLASSIC"
iniset $CONF_FILE DEFAULT external_network "$EXTERNAL_NETWORK"
iniset $CONF_FILE oslo_concurrency lock_path "$EC2API_STATE_PATH"
iniset $CONF_FILE DEFAULT state_path "$DATA_DIR"
GROUP_AUTHTOKEN="keystone_authtoken"
iniset $CONF_FILE $GROUP_AUTHTOKEN signing_dir "$AUTH_CACHE_DIR"
@ -328,10 +330,6 @@ if [[ -f "$NOVA_CONF" ]]; then
iniset $CONF_FILE DEFAULT s3_url "$s3_proto://$s3_host:$s3_port"
fi
nova_state_path=$(iniget $NOVA_CONF DEFAULT state_path)
root_state_path=$(dirname $nova_state_path)
iniset $CONF_FILE DEFAULT state_path ${root_state_path}/ec2api
fi
#init cache dir
@ -340,6 +338,12 @@ sudo mkdir -p $AUTH_CACHE_DIR
sudo chown $USER $AUTH_CACHE_DIR
sudo rm -f $AUTH_CACHE_DIR/*
#init data dir
echo Creating data dir
sudo mkdir -p $DATA_DIR
sudo chown $USER $DATA_DIR
sudo rm -f $DATA_DIR/*
#install it
echo Installing package
if [[ -z "$VIRTUAL_ENV" ]]; then