New VIP-related fields in the database

* Add 'is_user_defined' field to 'ip_addrs' table.
* Rename 'vip_type' field to 'vip_name' of table 'ip_addrs'.
* Add 'vip_namespace' field to 'ip_addrs' table.
* Copy vip namespaces from plugin table network roles to 'vip_namespace' field
  according to the unique vip name.
* Add database migrations.
* Add unit test for migrations.

Change-Id: Ia3e1d7f6e08dbebcb182de75eeaf58ddf6be4a8d
Partial-bug: #1482399
This commit is contained in:
Ivan Kliuk 2015-12-05 17:16:38 +02:00 committed by Ilya Kutukov
parent b2a2d0799c
commit 169fae21b0
12 changed files with 433 additions and 74 deletions

View File

@ -164,11 +164,15 @@ NETWORK_INTERFACE_TYPES = Enum(
'bond'
)
NETWORK_VIP_TYPES = Enum(
NETWORK_VIP_NAMES_V6_1 = Enum(
'haproxy',
'vrouter',
)
VIP_NAME_MAX_LEN = 25
VIP_NAMESPACE_MAX_LEN = 25
NETWORK_GROUP_NAME_MAX_LEN = 50
BOND_MODES = Enum(
# same for both OVS and linux
'active-backup',

View File

@ -20,20 +20,25 @@ Create Date: 2015-12-15 17:20:49.519542
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
from oslo_serialization import jsonutils
revision = '11a9adc6d36a'
down_revision = '43b2cb64dae6'
from alembic import op # noqa
import sqlalchemy as sa # noqa
def upgrade():
add_foreign_key_ondelete()
upgrade_ip_address()
update_vips_from_network_roles()
def downgrade():
remove_foreign_key_ondelete()
downgrade_ip_address()
def remove_foreign_key_ondelete():
@ -419,3 +424,114 @@ def add_foreign_key_ondelete():
['parent_id'], ['id'],
ondelete='CASCADE'
)
def upgrade_ip_address():
op.add_column(
'ip_addrs',
sa.Column(
'is_user_defined',
sa.Boolean,
nullable=False,
default=False,
server_default="false"
)
)
op.add_column(
'ip_addrs',
sa.Column(
'vip_namespace',
sa.String(length=25),
nullable=True,
default=None,
server_default=None
)
)
op.alter_column(
'ip_addrs',
'vip_type',
new_column_name='vip_name',
type_=sa.String(length=25)
)
def update_vips_from_network_roles():
def _update_network_roles_from_db_metadata(query):
connection = op.get_bind()
_vip_name_to_vip_data = {}
select = sa.text(query)
network_roles_metadata = connection.execute(select)
for network_roles_json in network_roles_metadata:
if not network_roles_json or not network_roles_json[0]:
continue
network_roles = jsonutils.loads(network_roles_json[0])
# warning: in current schema it is possible that network
# role is declared as dict
if isinstance(network_roles, dict):
network_roles = [network_roles]
for network_role in network_roles:
vips = network_role.get('properties', {}).get('vip', [])
for vip in vips:
_vip_name_to_vip_data[vip['name']] = vip
return _vip_name_to_vip_data
roles_vip_name_to_vip_data = {}
# get namespaces from plugins
roles_vip_name_to_vip_data.update(
_update_network_roles_from_db_metadata(
"SELECT network_roles_metadata from plugins"
)
)
# get namespaces from releases
roles_vip_name_to_vip_data.update(
_update_network_roles_from_db_metadata(
"SELECT network_roles_metadata from releases"
)
)
# perform update
connection = op.get_bind()
ip_addrs_select = sa.text(
"SELECT id, vip_name from ip_addrs"
)
ip_addrs = connection.execute(ip_addrs_select)
ip_addrs_update = sa.sql.text(
"UPDATE ip_addrs "
"SET vip_namespace = :vip_namespace WHERE id = :id"
)
existing_names_to_id = dict(
(vip_name, vip_id) for (vip_id, vip_name) in ip_addrs
)
for vip_name in existing_names_to_id:
namespace = roles_vip_name_to_vip_data\
.get(vip_name, {}).get('namespace')
# update only if namespace arrived
if namespace:
connection.execute(
ip_addrs_update,
id=existing_names_to_id[vip_name],
vip_namespace=namespace
)
def downgrade_ip_address():
op.alter_column(
'ip_addrs',
'vip_name',
new_column_name='vip_type',
type_=sa.String(length=25)
)
op.drop_column('ip_addrs', 'is_user_defined')
op.drop_column('ip_addrs', 'vip_namespace')

View File

@ -13,7 +13,7 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from sqlalchemy import Boolean
from sqlalchemy import Column
from sqlalchemy.dialects import postgresql as psql
from sqlalchemy.ext.mutable import MutableDict
@ -22,6 +22,7 @@ from sqlalchemy import Integer
from sqlalchemy.orm import relationship
from sqlalchemy import String
from nailgun import consts
from nailgun.db.sqlalchemy.models.base import Base
from nailgun.db.sqlalchemy.models.fields import JSON
@ -33,8 +34,18 @@ class IPAddr(Base):
ondelete="CASCADE"))
node = Column(Integer, ForeignKey('nodes.id', ondelete="CASCADE"))
ip_addr = Column(psql.INET, nullable=False)
vip_type = Column(String(25), nullable=True)
vip_name = Column(String(consts.VIP_NAME_MAX_LEN), nullable=True)
vip_namespace = Column(
String(consts.VIP_NAMESPACE_MAX_LEN),
nullable=True,
server_default=None
)
is_user_defined = Column(
Boolean,
nullable=False,
default=False,
server_default="false"
)
network_data = relationship("NetworkGroup")
node_data = relationship("Node")
@ -52,7 +63,7 @@ class NetworkGroup(Base):
__tablename__ = 'network_groups'
id = Column(Integer, primary_key=True)
name = Column(String(50), nullable=False)
name = Column(String(consts.NETWORK_GROUP_NAME_MAX_LEN), nullable=False)
# can be nullable only for fuelweb admin net
release = Column(Integer, ForeignKey('releases.id', ondelete='CASCADE'))
# can be nullable only for fuelweb admin net

View File

@ -135,11 +135,11 @@ class UpgradeHelper(object):
renamed_vips = collections.defaultdict(dict)
for ng_name, vips in six.iteritems(vips):
ng_vip_rules = rename_vip_rules[ng_name]
for vip_type, vip_addr in six.iteritems(vips):
if vip_type not in ng_vip_rules:
for vip_name, vip_addr in six.iteritems(vips):
if vip_name not in ng_vip_rules:
continue
new_vip_type = ng_vip_rules[vip_type]
renamed_vips[ng_name][new_vip_type] = vip_addr
new_vip_name = ng_vip_rules[vip_name]
renamed_vips[ng_name][new_vip_name] = vip_addr
return renamed_vips
@classmethod
@ -159,7 +159,7 @@ class UpgradeHelper(object):
consts.NETWORKS.management):
vips.pop(ng_name)
# NOTE(akscram): In the 7.0 release was introduced networking
# templates that use the vip_type column as
# templates that use the vip_name column as
# unique names of VIPs.
if version.LooseVersion(orig_cluster.release.environment_version) < \
version.LooseVersion("7.0"):

View File

@ -301,12 +301,12 @@ class NetworkManager(object):
db().flush()
@classmethod
def get_assigned_vip(cls, nodegroup, network_name, vip_type):
def get_assigned_vip(cls, nodegroup, network_name, vip_name):
"""Get VIP address, if it was assigned already
:param nodegroup: Name of the node group.
:param nerwork_name: Name of a network the VIP is allocated in.
:param vip_type: Type of a required VIP.
:param vip_name: Type of a required VIP.
:returns: IP address of a VIP that matches specified criterias.
None, if no VIP matches specificied criterias.
@ -314,7 +314,7 @@ class NetworkManager(object):
network = cls.get_network_by_name_and_nodegroup(network_name,
nodegroup)
cluster_vip_q = db().query(IPAddr).filter_by(vip_type=vip_type)
cluster_vip_q = db().query(IPAddr).filter_by(vip_name=vip_name)
if network is not None:
cluster_vip_q = cluster_vip_q.filter_by(network=network.id)
@ -325,8 +325,7 @@ class NetworkManager(object):
return cluster_vip.ip_addr
@classmethod
def assign_vip(cls, nodegroup, network_name,
vip_type=consts.NETWORK_VIP_TYPES.haproxy):
def assign_vip(cls, nodegroup, network_name, vip_name):
"""Idempotent assignment of VirtualIP addresses to nodegroup.
Returns VIP for given nodegroup and network.
@ -342,14 +341,14 @@ class NetworkManager(object):
:type nodegroup: NodeGroup model
:param network_name: Network name
:type network_name: str
:param vip_type: Type of VIP
:type vip_type: str
:param vip_name: Name of VIP
:type vip_name: str
:returns: assigned VIP (string)
:raises: Exception
"""
already_assigned = cls.get_assigned_vip(nodegroup,
network_name, vip_type)
network_name, vip_name)
network = cls.get_network_by_name_and_nodegroup(network_name,
nodegroup)
@ -365,7 +364,7 @@ class NetworkManager(object):
cluster_vip = db().query(IPAddr).filter_by(
network=network.id,
node=None,
vip_type=vip_type
vip_name=vip_name
).first()
ips_in_use = None
@ -380,7 +379,7 @@ class NetworkManager(object):
# IP address has not been assigned, let's do it
vip = cls.get_free_ips(network, ips_in_use=ips_in_use)[0]
ne_db = IPAddr(network=network.id, ip_addr=vip, vip_type=vip_type)
ne_db = IPAddr(network=network.id, ip_addr=vip, vip_name=vip_name)
# delete stalled VIP address after new one was found.
if cluster_vip:
@ -403,14 +402,14 @@ class NetworkManager(object):
nodegroup = objects.Cluster.get_controllers_node_group(cluster)
for ng in cluster.network_groups:
for vip_type in ng.meta.get('vips', ()):
for vip_name in ng.meta.get('vips', ()):
# used for backwards compatibility
if vip_type == consts.NETWORK_VIP_TYPES.haproxy:
if vip_name == consts.NETWORK_VIP_NAMES_V6_1.haproxy:
key = '{0}_vip'.format(ng.name)
else:
key = '{0}_{1}_vip'.format(ng.name, vip_type)
key = '{0}_{1}_vip'.format(ng.name, vip_name)
result[key] = cls.assign_vip(nodegroup, ng.name, vip_type)
result[key] = cls.assign_vip(nodegroup, ng.name, vip_name)
return result
@ -419,7 +418,7 @@ class NetworkManager(object):
node_group_id = objects.Cluster.get_controllers_group_id(cluster)
cluster_vips = db.query(IPAddr).join(IPAddr.network_data).filter(
IPAddr.node.is_(None) &
IPAddr.vip_type.isnot(None) &
IPAddr.vip_name.isnot(None) &
(NetworkGroup.group_id == node_group_id)
)
return cluster_vips
@ -435,7 +434,7 @@ class NetworkManager(object):
cluster_vips = cls._get_assigned_vips_for_net_groups(cluster)
vips = defaultdict(dict)
for vip in cluster_vips:
vips[vip.network_data.name][vip.vip_type] = vip.ip_addr
vips[vip.network_data.name][vip.vip_name] = vip.ip_addr
return vips
@classmethod
@ -455,12 +454,12 @@ class NetworkManager(object):
cluster_vips = cls._get_assigned_vips_for_net_groups(cluster)
assigned_vips = defaultdict(dict)
for vip in cluster_vips:
assigned_vips[vip.network_data.name][vip.vip_type] = vip
assigned_vips[vip.network_data.name][vip.vip_name] = vip
for net_group in cluster.network_groups:
if net_group.name not in vips:
continue
assigned_vips_by_type = assigned_vips.get(net_group.name, {})
for vip_type, ip_addr in six.iteritems(vips[net_group.name]):
for vip_name, ip_addr in six.iteritems(vips[net_group.name]):
if not cls.check_ip_belongs_to_net(ip_addr, net_group):
ranges = [(rng.first, rng.last)
for rng in net_group.ip_ranges]
@ -470,14 +469,14 @@ class NetworkManager(object):
"ranges {3} of the cluster \"{4}\"."
.format(ip_addr, net_group.id, net_group.name, ranges,
cluster.id))
if vip_type in assigned_vips_by_type:
assigned_vip = assigned_vips_by_type[vip_type]
if vip_name in assigned_vips_by_type:
assigned_vip = assigned_vips_by_type[vip_name]
assigned_vip.ip_addr = ip_addr
else:
vip = IPAddr(
network=net_group.id,
ip_addr=ip_addr,
vip_type=vip_type,
vip_name=vip_name,
)
db().add(vip)
db().flush()
@ -1708,7 +1707,7 @@ class NetworkManager(object):
x[0] for x in
db().query(IPAddr.ip_addr).filter(
IPAddr.network == network_id,
or_(IPAddr.node.isnot(None), IPAddr.vip_type.isnot(None))
or_(IPAddr.node.isnot(None), IPAddr.vip_name.isnot(None))
)
]
@ -1806,7 +1805,7 @@ class AllocateVIPs70Mixin(object):
cluster_db, node_group.name)
net_group = cls.get_network_group_for_role(
net_role, net_group_mapping)
return cls.assign_vip(node_group, net_group, vip_type='public')
return cls.assign_vip(node_group, net_group, vip_name='public')
@classmethod
def _assign_vips_for_net_groups(cls, cluster):

View File

@ -716,11 +716,19 @@ class TestNovaNetworkConfigurationHandlerHA(BaseIntegrationTest):
self.assertEqual(
resp['management_vip'],
self.net_manager.assign_vip(nodegroup, 'management'))
self.net_manager.assign_vip(
nodegroup,
'management',
consts.NETWORK_VIP_NAMES_V6_1.haproxy
))
self.assertEqual(
resp['public_vip'],
self.net_manager.assign_vip(nodegroup, 'public'))
self.net_manager.assign_vip(
nodegroup,
'public',
consts.NETWORK_VIP_NAMES_V6_1.haproxy
))
class TestAdminNetworkConfiguration(BaseIntegrationTest):

View File

@ -55,12 +55,12 @@ class BaseNetworkManagerTest(BaseIntegrationTest):
for net_group in cluster.network_groups:
if net_group.name not in rules:
continue
vips_by_types = rules[net_group.name]
for vip_type, ip_addr in vips_by_types.items():
vips_by_names = rules[net_group.name]
for vip_name, ip_addr in vips_by_names.items():
ip = IPAddr(
network=net_group.id,
ip_addr=ip_addr,
vip_type=vip_type,
vip_name=vip_name,
)
self.db.add(ip)
created_ips.append(ip)
@ -195,9 +195,15 @@ class TestNetworkManager(BaseNetworkManagerTest):
self.env.clusters[0])
vip = self.env.network_manager.assign_vip(
nodegroup, consts.NETWORKS.management)
nodegroup,
consts.NETWORKS.management,
consts.NETWORK_VIP_NAMES_V6_1.haproxy
)
vip2 = self.env.network_manager.assign_vip(
nodegroup, consts.NETWORKS.management)
nodegroup,
consts.NETWORKS.management,
consts.NETWORK_VIP_NAMES_V6_1.haproxy
)
self.assertEqual(vip, vip2)
@ -206,8 +212,10 @@ class TestNetworkManager(BaseNetworkManagerTest):
nodegroup = objects.Cluster.get_controllers_node_group(
self.env.clusters[0])
self.env.network_manager.assign_vip(nodegroup,
consts.NETWORKS.fuelweb_admin)
self.env.network_manager.assign_vip(
nodegroup,
consts.NETWORKS.fuelweb_admin,
consts.NETWORK_VIP_NAMES_V6_1.haproxy)
def test_assign_vip_throws_not_found_exception(self):
self.env.create_cluster(api=True)
@ -219,7 +227,9 @@ class TestNetworkManager(BaseNetworkManagerTest):
"Network 'non-existing-network' for nodegroup='[\w-]+' not found.",
self.env.network_manager.assign_vip,
nodegroup,
'non-existing-network')
'non-existing-network',
consts.NETWORK_VIP_NAMES_V6_1.haproxy
)
def test_vip_for_admin_network_is_free(self):
admin_net_id = self.env.network_manager.get_admin_network_group_id()
@ -252,7 +262,8 @@ class TestNetworkManager(BaseNetworkManagerTest):
self.env.network_manager.assign_admin_ips(cluster.nodes)
admin_vip = self.env.network_manager.assign_vip(
objects.Cluster.get_controllers_node_group(cluster),
consts.NETWORKS.fuelweb_admin
consts.NETWORKS.fuelweb_admin,
consts.NETWORK_VIP_NAMES_V6_1.haproxy
)
node_ips = [n.ip for n in self.env.nodes]
@ -548,12 +559,12 @@ class TestNetworkManager(BaseNetworkManagerTest):
def test_get_assigned_vips(self):
vips_to_create = {
consts.NETWORKS.management: {
consts.NETWORK_VIP_TYPES.haproxy: '192.168.0.1',
consts.NETWORK_VIP_TYPES.vrouter: '192.168.0.2',
consts.NETWORK_VIP_NAMES_V6_1.haproxy: '192.168.0.1',
consts.NETWORK_VIP_NAMES_V6_1.vrouter: '192.168.0.2',
},
consts.NETWORKS.public: {
consts.NETWORK_VIP_TYPES.haproxy: '172.16.0.2',
consts.NETWORK_VIP_TYPES.vrouter: '172.16.0.3',
consts.NETWORK_VIP_NAMES_V6_1.haproxy: '172.16.0.2',
consts.NETWORK_VIP_NAMES_V6_1.vrouter: '172.16.0.3',
},
}
cluster = self.env.create_cluster(api=False)
@ -564,20 +575,20 @@ class TestNetworkManager(BaseNetworkManagerTest):
def test_assign_given_vips_for_net_groups(self):
vips_to_create = {
consts.NETWORKS.management: {
consts.NETWORK_VIP_TYPES.haproxy: '192.168.0.1',
consts.NETWORK_VIP_NAMES_V6_1.haproxy: '192.168.0.1',
},
consts.NETWORKS.public: {
consts.NETWORK_VIP_TYPES.haproxy: '172.16.0.2',
consts.NETWORK_VIP_NAMES_V6_1.haproxy: '172.16.0.2',
},
}
vips_to_assign = {
consts.NETWORKS.management: {
consts.NETWORK_VIP_TYPES.haproxy: '192.168.0.1',
consts.NETWORK_VIP_TYPES.vrouter: '192.168.0.2',
consts.NETWORK_VIP_NAMES_V6_1.haproxy: '192.168.0.1',
consts.NETWORK_VIP_NAMES_V6_1.vrouter: '192.168.0.2',
},
consts.NETWORKS.public: {
consts.NETWORK_VIP_TYPES.haproxy: '172.16.0.4',
consts.NETWORK_VIP_TYPES.vrouter: '172.16.0.5',
consts.NETWORK_VIP_NAMES_V6_1.haproxy: '172.16.0.4',
consts.NETWORK_VIP_NAMES_V6_1.vrouter: '172.16.0.5',
},
}
cluster = self.env.create_cluster(api=False)
@ -600,7 +611,7 @@ class TestNetworkManager(BaseNetworkManagerTest):
def test_assign_given_vips_for_net_groups_assign_error(self):
vips_to_assign = {
consts.NETWORKS.management: {
consts.NETWORK_VIP_TYPES.haproxy: '10.10.0.1',
consts.NETWORK_VIP_NAMES_V6_1.haproxy: '10.10.0.1',
},
}
expected_msg_regexp = '^Cannot assign VIP with the address "10.10.0.1"'
@ -1155,7 +1166,7 @@ class TestNeutronManager70(BaseNetworkManagerTest):
endpoint_ip = self.net_manager.get_end_point_ip(self.cluster.id)
assign_vip_mock.assert_called_once_with(
objects.Cluster.get_controllers_node_group(self.cluster),
mock.ANY, vip_type='public')
mock.ANY, vip_name='public')
self.assertEqual(endpoint_ip, vip)
def test_assign_vips_for_net_groups_for_api(self):
@ -1276,7 +1287,7 @@ class TestNovaNetworkManager70(TestNeutronManager70):
endpoint_ip = self.net_manager.get_end_point_ip(self.cluster.id)
assign_vip_mock.assert_called_once_with(
objects.Cluster.get_controllers_node_group(self.cluster),
mock.ANY, vip_type='public')
mock.ANY, vip_name='public')
self.assertEqual(endpoint_ip, vip)

View File

@ -23,8 +23,6 @@ from nailgun.test import base
class TestDbMigrations(base.BaseTestCase):
def test_clean_downgrade(self):
# We don't have data migration for clusters with vip_type 'ovs'
# so checking migration only for clean DB
dropdb()
alembic.command.upgrade(ALEMBIC_CONFIG, 'head')
alembic.command.downgrade(ALEMBIC_CONFIG, 'base')

View File

@ -145,7 +145,7 @@ class TestVipTypesMigration(base.BaseAlembicMigrationTest):
sa.select([ip_addrs_table.c.vip_type]).where(
ip_addrs_table.c.ip_addr == '192.168.0.2')
).first()
self.assertEqual(ip_addr[0], consts.NETWORK_VIP_TYPES.haproxy)
self.assertEqual(ip_addr[0], consts.NETWORK_VIP_NAMES_V6_1.haproxy)
def test_vip_type_in_releases(self):
releases_table = self.meta.tables['releases']
@ -158,7 +158,7 @@ class TestVipTypesMigration(base.BaseAlembicMigrationTest):
neutron = networks_meta['neutron']['networks'][0]
self.assertItemsEqual(
neutron.get('vips'),
[consts.NETWORK_VIP_TYPES.haproxy])
[consts.NETWORK_VIP_NAMES_V6_1.haproxy])
nova_network = networks_meta['nova_network']['networks'][0]
self.assertIsNone(nova_network.get('vips'))
@ -171,7 +171,7 @@ class TestVipTypesMigration(base.BaseAlembicMigrationTest):
).first()[0]
vips = jsonutils.loads(meta).get('vips')
self.assertEqual(vips, [consts.NETWORK_VIP_TYPES.haproxy])
self.assertEqual(vips, [consts.NETWORK_VIP_NAMES_V6_1.haproxy])
class TestRepoMetadataToRepoSetup(base.BaseAlembicMigrationTest):

View File

@ -12,15 +12,18 @@
# License for the specific language governing permissions and limitations
# under the License.
_prepare_revision = '43b2cb64dae6'
_test_revision = '11a9adc6d36a'
import alembic
from oslo_serialization import jsonutils
import sqlalchemy as sa
from nailgun.db import db
from nailgun.db import dropdb
from nailgun.db.migration import ALEMBIC_CONFIG
from nailgun.test import base
_prepare_revision = '43b2cb64dae6'
_test_revision = '11a9adc6d36a'
def setup_module():
dropdb()
@ -30,7 +33,185 @@ def setup_module():
def prepare():
pass
meta = base.reflect_db_metadata()
result = db.execute(
meta.tables['releases'].insert(),
[{
'name': 'test_name',
'version': '2015.1-8.0',
'operating_system': 'ubuntu',
'state': 'available',
'networks_metadata': jsonutils.dumps({
'neutron': {
'networks': [
{
'assign_vip': True,
},
]
},
'nova_network': {
'networks': [
{
'assign_vip': False,
},
]
},
}),
'network_roles_metadata': jsonutils.dumps([{
'id': 'admin/vip',
'default_mapping': 'fuelweb_admin',
'properties': {
'subnet': True,
'gateway': False,
'vip': [
{
'name': 'release-vip1',
},
{
'name': 'release-vip2',
'namespace': 'release-vip2-namespace'
}
]
}
}]),
'is_deployable': True,
}])
releaseid = result.inserted_primary_key[0]
db.execute(
meta.tables['clusters'].insert(),
[{
'name': 'test_env',
'release_id': releaseid,
'mode': 'ha_compact',
'status': 'new',
'net_provider': 'neutron',
'grouping': 'roles',
'fuel_version': '8.0',
}])
db.execute(
meta.tables['ip_addrs'].insert(),
[
{
'ip_addr': '192.168.0.2',
'vip_type': 'management'
},
{
'ip_addr': '192.168.1.2',
'vip_type': 'haproxy'
},
{
'ip_addr': '192.168.11.2',
'vip_type': 'my-vip1',
'namespace': 'my-namespace1'
},
{
'ip_addr': '192.168.12.2',
'vip_type': 'my-vip2',
'namespace': 'my-namespace2'
},
{
'ip_addr': '192.168.13.2',
'vip_type': 'my-vip3',
'namespace': 'my-namespace3'
},
{
'ip_addr': '192.168.14.2',
'vip_type': 'my-vip4',
'namespace': 'my-namespace4'
},
{
'ip_addr': '192.168.15.2',
'vip_type': 'release-vip2'
}
])
db.execute(
meta.tables['network_groups'].insert(),
[{
'name': 'public',
'release': releaseid,
'meta': jsonutils.dumps({'assign_vip': True})
}])
db.execute(
meta.tables['plugins'].insert(),
[{
'name': 'test_plugin_a',
'title': 'Test plugin A',
'version': '2.0.0',
'description': 'Test plugin A for Fuel',
'homepage': 'http://fuel_plugins.test_plugin.com',
'package_version': '4.0.0',
'groups': jsonutils.dumps(['tgroup']),
'authors': jsonutils.dumps(['tauthor']),
'licenses': jsonutils.dumps(['tlicense']),
'releases': jsonutils.dumps([
{'repository_path': 'repositories/ubuntu'}
]),
'fuel_version': jsonutils.dumps(['8.0']),
'network_roles_metadata': jsonutils.dumps([{
'id': 'admin/vip',
'default_mapping': 'fuelweb_admin',
'properties': {
'subnet': True,
'gateway': False,
'vip': [
{
'name': 'my-vip1',
'namespace': 'my-namespace1',
},
{
'name': 'my-vip2',
'namespace': 'my-namespace2',
}
]
}
}])
}]
)
db.execute(
meta.tables['plugins'].insert(),
[{
'name': 'test_plugin_b',
'title': 'Test plugin B',
'version': '2.0.0',
'description': 'Test plugin B for Fuel',
'homepage': 'http://fuel_plugins.test_plugin.com',
'package_version': '4.0.0',
'groups': jsonutils.dumps(['tgroup']),
'authors': jsonutils.dumps(['tauthor']),
'licenses': jsonutils.dumps(['tlicense']),
'releases': jsonutils.dumps([
{'repository_path': 'repositories/ubuntu'}
]),
'fuel_version': jsonutils.dumps(['8.0']),
'network_roles_metadata': jsonutils.dumps([{
'id': 'admin/vip',
'default_mapping': 'fuelweb_admin',
'properties': {
'subnet': True,
'gateway': False,
'vip': [
{
'name': 'my-vip3',
'namespace': 'my-namespace3',
},
{
'name': 'my-vip4',
'namespace': 'my-namespace4',
}
]
}
}])
}]
)
db.commit()
class TestNodeGroupsMigration(base.BaseAlembicMigrationTest):
@ -90,3 +271,31 @@ class TestNodeGroupsMigration(base.BaseAlembicMigrationTest):
if constraint.name in fkeys:
value = fkeys[constraint.name]
self.assertEqual(constraint.ondelete, value)
class TestVipMigration(base.BaseAlembicMigrationTest):
def test_ip_addrs_vip_name_exists(self):
result = db.execute(
sa.select([self.meta.tables['ip_addrs'].c.vip_name]))
self.assertEqual(result.scalar(), "management")
def test_ip_addrs_vip_namespace_exists(self):
result = db.execute(
sa.select([
self.meta.tables['ip_addrs'].c.vip_name,
self.meta.tables['ip_addrs'].c.vip_namespace
]))
result = list(result)
self.assertItemsEqual(
(
('management', None,),
('haproxy', None,),
('my-vip1', 'my-namespace1',),
('my-vip2', 'my-namespace2',),
('my-vip3', 'my-namespace3',),
('my-vip4', 'my-namespace4',),
# namespace has appeared from release network role
('release-vip2', 'release-vip2-namespace',),
),
result
)

View File

@ -77,7 +77,7 @@ class TestNetworkConfigurationValidatorProtocol(
"configurable": True,
"floating_range_var": "floating_ranges",
"ext_net_data": [],
"vips": consts.NETWORK_VIP_TYPES,
"vips": consts.NETWORK_VIP_NAMES_V6_1,
},
"name": consts.NETWORKS.public,
"vlan_start": None

View File

@ -442,13 +442,16 @@ def upgrade_vip_types_6_0_to_6_1(connection):
"UPDATE ip_addrs SET vip_type = :haproxy WHERE node IS NULL")
connection.execute(update_query_node_null,
haproxy=consts.NETWORK_VIP_TYPES.haproxy)
haproxy=consts.NETWORK_VIP_NAMES_V6_1.haproxy)
def downgrade_vip_types_6_1_to_6_0(connection):
delete_query = text(
"DELETE FROM ip_addrs WHERE vip_type != :haproxy AND node IS NULL")
connection.execute(delete_query, haproxy=consts.NETWORK_VIP_TYPES.haproxy)
connection.execute(
delete_query,
haproxy=consts.NETWORK_VIP_NAMES_V6_1.haproxy
)
def upgrade_6_0_to_6_1_plugins_cluster_attrs_use_ids_mapping(connection):
@ -542,7 +545,7 @@ def upgrade_network_groups_metadata_6_0_to_6_1(connection):
def create_default_vips(network):
if "assign_vip" in network:
if network["assign_vip"]:
network["vips"] = [consts.NETWORK_VIP_TYPES.haproxy]
network["vips"] = [consts.NETWORK_VIP_NAMES_V6_1.haproxy]
del network["assign_vip"]