Support tacker-db-manage for Multi DB backend

The current Tacker implementation does not support PostgreSQL,
and differences between the MySQL and PostgreSQL implementations can
cause errors in practice.
This patch implements PostgreSQL support through Tacker
and tacker-db-manage.

Note that UT/FT is not added because conventional tests can cover
this patch.

Implements: blueprint support-multidb-backend
Change-Id: Ide07befdb26f6b173d1185fe0dbcf59f8f3dbf81
This commit is contained in:
Tetsuro Kaneko 2023-01-27 06:25:39 +00:00 committed by Yuta Kazato
parent 844de7fb20
commit 33ca6ad556
25 changed files with 449 additions and 122 deletions

View File

@ -174,6 +174,76 @@ So the first step of installing tacker is to clone Devstack and prepare your
$ ./stack.sh
Use PostgreSQL as Tacker database
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
When installing via Devstack, MySQL is used as Tacker database backend
by default.
To use PostgreSQL as Tacker database backend, execute the following command.
#. Install PostgreSQL and login.
.. code-block:: console
$ sudo apt install postgresql postgresql-contrib
$ sudo -i -u postgres
$ psql
#. Create PostgreSQL database and user.
.. code-block::
CREATE DATABASE tacker;
CREATE ROLE tacker WITH CREATEDB LOGIN PASSWORD '<TACKERDB_PASSWORD>';
exit;
#. Modify ``postgresql.conf`` and restart PostgreSQL server.
.. note::
The location of ``postgresql.conf`` is different for each distribution.
For Ubuntu distribution, modify
``/etc/postgresql/{POSTGRESQL_VERSION}/main/postgresql.conf``.
Insert ``escape`` as the value of ``bytea_output`` in ``postgresql.conf``.
.. code-block:: ini
bytea_output = 'escape'
Restart PostgreSQL server.
.. code-block:: console
$ sudo service postgresql restart
#. Modify ``tacker.conf`` for PostgreSQL and restart Tacker server.
Edit the configuration of [database] in ``/etc/tacker/tacker.conf``
as follows.
.. code-block:: ini
[database]
connection = postgresql://tacker:<POSTGRES_PASSWORD>@<POSTGRES_IP>/tacker?client_encoding=utf8
Restart Tacker server.
.. code-block:: console
$ sudo systemctl restart devstack@tacker.service
$ sudo systemctl restart devstack@tacker-conductor.service
#. Populate Tacker database.
.. code-block:: console
$ /usr/local/bin/tacker-db-manage \
--config-file /etc/tacker/tacker.conf \
upgrade head
.. rubric:: Footnotes
.. [#f0] https://docs.openstack.org/devstack/latest/

View File

@ -64,7 +64,13 @@ Installing Tacker Server
.. note::
Make sure to replace the ``<branch_name>`` in command examples with
specific branch name, such as ``stable/ussuri``.
specific branch name, such as ``stable/2023.1``.
.. note::
From Tacker Antelope, PostgreSQL is available as a Tacker DB.
If you would like to use PostgreSQL, please see
`Install via Devstack`_ document.
#. Create MySQL database and user.
@ -328,3 +334,5 @@ required because the console will be locked by a running process.
$ sudo systemctl start tacker-conductor.service
.. _Install via Devstack: https://docs.openstack.org/tacker/latest/install/devstack.html

View File

@ -0,0 +1,6 @@
features:
- |
Support Tacker and tacker-db-manage for Multi DB backend,
especially PostgreSQL.
Use PostgreSQL as Tacker database via Devstack
for Tacker Installation.

View File

@ -187,7 +187,7 @@ class VnfPackage(model_base.BASE, models.SoftDeleteMixin,
VnfPackageVnfd, uselist=False,
primaryjoin='and_(VnfPackage.id == '
'VnfPackageVnfd.package_uuid,'
'VnfPackageVnfd.deleted == 0)')
'VnfPackageVnfd.deleted == "0")')
vnf_artifacts = orm.relationship(
VnfPackageArtifactInfo,

View File

@ -34,6 +34,13 @@ from tacker.db import types
def upgrade(active_plugins=None, options=None):
bind = op.get_bind()
engine = bind.engine
if engine.name == 'postgresql':
text_type_maxlen = sa.VARCHAR(length=65535)
else:
text_type_maxlen = sa.TEXT(length=65535)
op.create_table('nsd',
sa.Column('tenant_id', sa.String(length=64), nullable=False),
sa.Column('id', types.Uuid(length=36), nullable=False),
@ -56,8 +63,8 @@ def upgrade(active_plugins=None, options=None):
sa.Column('vim_id', sa.String(length=64), nullable=False),
sa.Column('name', sa.String(length=255), nullable=False),
sa.Column('description', sa.Text(), nullable=True),
sa.Column('vnf_ids', sa.TEXT(length=65535), nullable=True),
sa.Column('mgmt_urls', sa.TEXT(length=65535), nullable=True),
sa.Column('vnf_ids', text_type_maxlen, nullable=True),
sa.Column('mgmt_urls', text_type_maxlen, nullable=True),
sa.Column('status', sa.String(length=64), nullable=False),
sa.Column('error_reason', sa.Text(), nullable=True),
sa.ForeignKeyConstraint(['nsd_id'], ['nsd.id'], ),
@ -68,7 +75,7 @@ def upgrade(active_plugins=None, options=None):
sa.Column('id', types.Uuid(length=36), nullable=False),
sa.Column('nsd_id', types.Uuid(length=36), nullable=False),
sa.Column('key', sa.String(length=255), nullable=False),
sa.Column('value', sa.TEXT(length=65535), nullable=True),
sa.Column('value', text_type_maxlen, nullable=True),
sa.ForeignKeyConstraint(['nsd_id'], ['nsd.id'], ),
sa.PrimaryKeyConstraint('id'),
mysql_engine='InnoDB'

View File

@ -32,5 +32,12 @@ import sqlalchemy as sa
def upgrade(active_plugins=None, options=None):
bind = op.get_bind()
engine = bind.engine
if engine.name == 'postgresql':
text_type_maxlen = sa.VARCHAR(length=65535)
else:
text_type_maxlen = sa.TEXT(length=65535)
op.alter_column('deviceattributes',
'value', type_=sa.TEXT(65535), nullable=True)
'value', type_=text_type_maxlen, nullable=True)

View File

@ -34,6 +34,14 @@ from tacker.db import types
def upgrade(active_plugins=None, options=None):
bind = op.get_bind()
engine = bind.engine
if engine.name == 'postgresql':
deleted_type = sa.SmallInteger
else:
deleted_type = Boolean
op.create_table(
'placement_constraint',
sa.Column('id', types.Uuid(length=36), nullable=False),
@ -46,7 +54,7 @@ def upgrade(active_plugins=None, options=None):
sa.Column('created_at', sa.DateTime(), nullable=False),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.Column('deleted_at', sa.DateTime(), nullable=True),
sa.Column('deleted', Boolean, default=False),
sa.Column('deleted', deleted_type, default=False),
sa.PrimaryKeyConstraint('id'),
mysql_engine='InnoDB'
)

View File

@ -61,7 +61,8 @@ def upgrade(active_plugins=None, options=None):
sa.Enum('INSTANTIATE', 'SCALE', 'SCALE_TO_LEVEL',
'CHANGE_FLAVOUR', 'TERMINATE', 'HEAL', 'OPERATE',
'CHANGE_EXT_CONN', 'CREATE_SNAPSHOT',
'REVERT_TO_SNAPSHOT', 'CHANGE_VNFPKG'),
'REVERT_TO_SNAPSHOT', 'CHANGE_VNFPKG',
name='operation'),
nullable=False),
sa.Column('isAutomaticInvocation', sa.Boolean(), nullable=False),
sa.Column('instantiationLevelId', sa.String(length=255),

View File

@ -34,8 +34,15 @@ from tacker.db import types
def upgrade(active_plugins=None, options=None):
bind = op.get_bind()
engine = bind.engine
if engine.name == 'postgresql':
text_type_maxlen = sa.VARCHAR(length=65535)
else:
text_type_maxlen = sa.TEXT(length=65535)
op.add_column('ns', sa.Column(
'vnffg_ids', sa.TEXT(length=65535), nullable=True))
'vnffg_ids', text_type_maxlen, nullable=True))
op.add_column('vnffgs', sa.Column(
'ns_id', types.Uuid(length=36), nullable=True))
op.create_foreign_key('vnffg_foreign_key',

View File

@ -35,6 +35,13 @@ from tacker.db.types import Json
def upgrade(active_plugins=None, options=None):
bind = op.get_bind()
engine = bind.engine
if engine.name == 'postgresql':
deleted_type = sa.SmallInteger
else:
deleted_type = sa.Boolean
op.create_table(
'vnffgtemplates',
sa.Column('id', sa.String(length=36), nullable=False),
@ -68,7 +75,7 @@ def upgrade(active_plugins=None, options=None):
sa.Column('name', sa.String(length=255), nullable=False),
sa.Column('status', sa.String(length=255), nullable=False),
sa.Column('path_id', sa.String(length=255), nullable=False),
sa.Column('symmetrical', sa.Boolean, default=False),
sa.Column('symmetrical', deleted_type, default=False),
sa.ForeignKeyConstraint(['vnffg_id'], ['vnffgs.id'], ),
sa.PrimaryKeyConstraint('id'),
mysql_engine='InnoDB'
@ -82,7 +89,7 @@ def upgrade(active_plugins=None, options=None):
sa.Column('nfp_id', sa.String(length=36), nullable=False),
sa.Column('status', sa.String(length=255), nullable=False),
sa.Column('path_id', sa.String(length=255), nullable=False),
sa.Column('symmetrical', sa.Boolean, default=False),
sa.Column('symmetrical', deleted_type, default=False),
sa.Column('chain', Json),
sa.ForeignKeyConstraint(['nfp_id'], ['vnffgnfps.id'], ),
sa.PrimaryKeyConstraint('id'),

View File

@ -32,5 +32,12 @@ import sqlalchemy as sa
def upgrade(active_plugins=None, options=None):
bind = op.get_bind()
engine = bind.engine
if engine.name == 'postgresql':
text_type_maxlen = sa.VARCHAR(length=65535)
else:
text_type_maxlen = sa.TEXT(length=65535)
op.alter_column('devicetemplateattributes',
'value', type_=sa.TEXT(65535), nullable=True)
'value', type_=text_type_maxlen, nullable=True)

View File

@ -30,22 +30,57 @@ from alembic import op # noqa: E402
def upgrade(active_plugins=None, options=None):
alter_sql_vnfd_ids = "ALTER TABLE vnf_lcm_filters CHANGE \
vnfd_ids vnfd_ids mediumtext GENERATED ALWAYS AS \
(json_unquote(json_extract(`filter`, \
'$.vnfInstanceSubscriptionFilter.vnfdIds'))) VIRTUAL;"
bind = op.get_bind()
engine = bind.engine
if engine.name == 'postgresql':
alter_sql_vnfd_ids_drop = "ALTER TABLE vnf_lcm_filters DROP \
COLUMN vnfd_ids;"
alter_sql_vnf_instance_ids = "ALTER TABLE vnf_lcm_filters CHANGE \
vnf_instance_ids vnf_instance_ids mediumtext GENERATED ALWAYS AS \
(json_unquote(json_extract(`filter`, \
'$.vnfInstanceSubscriptionFilter.vnfInstanceIds'))) VIRTUAL;"
alter_sql_vnfd_ids_add = "ALTER TABLE vnf_lcm_filters ADD \
COLUMN vnfd_ids text GENERATED ALWAYS AS (decode(filter->> \
'$.vnfInstanceSubscriptionFilter.vnfdIds','escape')) STORED;"
alter_sql_vnf_instance_names = "ALTER TABLE vnf_lcm_filters CHANGE \
vnf_instance_names vnf_instance_names mediumtext GENERATED \
alter_sql_vnf_instance_ids_drop = "ALTER TABLE vnf_lcm_filters DROP \
COLUMN vnf_instance_ids;"
alter_sql_vnf_instance_ids_add = "ALTER TABLE vnf_lcm_filters ADD \
vnf_instance_ids text GENERATED ALWAYS AS (decode(filter->> \
'$.vnfInstanceSubscriptionFilter.vnfInstanceIds','escape')) \
STORED;"
alter_sql_vnf_instance_names_drop = "ALTER TABLE vnf_lcm_filters \
DROP COLUMN vnf_instance_names;"
alter_sql_vnf_instance_names_add = "ALTER TABLE vnf_lcm_filters ADD \
COLUMN vnf_instance_names text GENERATED \
ALWAYS AS (decode(filter->> \
'$.vnfInstanceSubscriptionFilter.vnfInstanceNames', \
'escape')) STORED;"
op.execute(alter_sql_vnfd_ids_drop)
op.execute(alter_sql_vnfd_ids_add)
op.execute(alter_sql_vnf_instance_ids_drop)
op.execute(alter_sql_vnf_instance_ids_add)
op.execute(alter_sql_vnf_instance_names_drop)
op.execute(alter_sql_vnf_instance_names_add)
else:
alter_sql_vnfd_ids = "ALTER TABLE vnf_lcm_filters CHANGE \
vnfd_ids vnfd_ids mediumtext GENERATED ALWAYS AS \
(json_unquote(json_extract(`filter`, \
'$.vnfInstanceSubscriptionFilter.vnfdIds'))) VIRTUAL;"
alter_sql_vnf_instance_ids = "ALTER TABLE vnf_lcm_filters CHANGE \
vnf_instance_ids vnf_instance_ids mediumtext GENERATED \
ALWAYS AS (json_unquote(json_extract(`filter`, \
'$.vnfInstanceSubscriptionFilter.vnfInstanceNames'))) \
VIRTUAL;"
'$.vnfInstanceSubscriptionFilter.vnfInstanceIds'))) VIRTUAL;"
op.execute(alter_sql_vnfd_ids)
op.execute(alter_sql_vnf_instance_ids)
op.execute(alter_sql_vnf_instance_names)
alter_sql_vnf_instance_names = "ALTER TABLE vnf_lcm_filters CHANGE \
vnf_instance_names vnf_instance_names mediumtext \
GENERATED ALWAYS AS (json_unquote(json_extract(`filter`, \
'$.vnfInstanceSubscriptionFilter.vnfInstanceNames'))) \
VIRTUAL;"
op.execute(alter_sql_vnfd_ids)
op.execute(alter_sql_vnf_instance_ids)
op.execute(alter_sql_vnf_instance_names)

View File

@ -38,4 +38,9 @@ def upgrade(active_plugins=None, options=None):
# unique constraint is taken care by the nfvo_db plugin to support
# soft deletion of vim
op.drop_index('auth_url', table_name='vimauths')
bind = op.get_bind()
engine = bind.engine
if engine.name == 'postgresql':
op.drop_constraint('vimauths_auth_url_key', table_name='vimauths')
else:
op.drop_index('auth_url', table_name='vimauths')

View File

@ -36,6 +36,14 @@ from tacker.db import types
def upgrade(active_plugins=None, options=None):
bind = op.get_bind()
engine = bind.engine
if engine.name == 'postgresql':
deleted_type = sa.SmallInteger
else:
deleted_type = Boolean
op.create_table(
'vnf_instances',
sa.Column('id', types.Uuid(length=36), nullable=False),
@ -58,7 +66,7 @@ def upgrade(active_plugins=None, options=None):
sa.Column('created_at', sa.DateTime(), nullable=False),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.Column('deleted_at', sa.DateTime(), nullable=True),
sa.Column('deleted', sa.Boolean, default=False),
sa.Column('deleted', deleted_type, default=False),
sa.PrimaryKeyConstraint('id'),
mysql_engine='InnoDB'
)
@ -82,7 +90,7 @@ def upgrade(active_plugins=None, options=None):
sa.Column('created_at', sa.DateTime(), nullable=False),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.Column('deleted_at', sa.DateTime(), nullable=True),
sa.Column('deleted', Boolean, default=False),
sa.Column('deleted', deleted_type, default=False),
sa.PrimaryKeyConstraint('id'),
sa.ForeignKeyConstraint(['vnf_instance_id'],
['vnf_instances.id'], ),
@ -101,7 +109,7 @@ def upgrade(active_plugins=None, options=None):
sa.Column('created_at', sa.DateTime(), nullable=False),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.Column('deleted_at', sa.DateTime(), nullable=True),
sa.Column('deleted', Boolean, default=False),
sa.Column('deleted', deleted_type, default=False),
sa.PrimaryKeyConstraint('id'),
sa.ForeignKeyConstraint(['vnf_instance_id'],
['vnf_instances.id'], ),

View File

@ -35,6 +35,14 @@ from tacker.db import types
def upgrade(active_plugins=None, options=None):
bind = op.get_bind()
engine = bind.engine
if engine.name == 'postgresql':
deleted_type = sa.SmallInteger
else:
deleted_type = Boolean
op.create_table(
'vnf_packages',
sa.Column('id', types.Uuid(length=36), nullable=False),
@ -48,7 +56,7 @@ def upgrade(active_plugins=None, options=None):
sa.Column('created_at', sa.DateTime(), nullable=False),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.Column('deleted_at', sa.DateTime(), nullable=True),
sa.Column('deleted', Boolean, default=False),
sa.Column('deleted', deleted_type, default=False),
sa.PrimaryKeyConstraint('id'),
mysql_engine='InnoDB'
)
@ -61,7 +69,7 @@ def upgrade(active_plugins=None, options=None):
sa.Column('created_at', sa.DateTime(), nullable=False),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.Column('deleted_at', sa.DateTime(), nullable=True),
sa.Column('deleted', Boolean, default=False),
sa.Column('deleted', deleted_type, default=False),
sa.PrimaryKeyConstraint('id'),
sa.ForeignKeyConstraint(['package_uuid'],
['vnf_packages.id'], ),
@ -100,7 +108,7 @@ def upgrade(active_plugins=None, options=None):
sa.Column('created_at', sa.DateTime(), nullable=False),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.Column('deleted_at', sa.DateTime(), nullable=True),
sa.Column('deleted', Boolean, default=False),
sa.Column('deleted', deleted_type, default=False),
sa.PrimaryKeyConstraint('id'),
sa.ForeignKeyConstraint(['package_uuid'],
['vnf_packages.id'], ),
@ -125,7 +133,7 @@ def upgrade(active_plugins=None, options=None):
sa.Column('created_at', sa.DateTime(), nullable=False),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.Column('deleted_at', sa.DateTime(), nullable=True),
sa.Column('deleted', Boolean, default=False),
sa.Column('deleted', deleted_type, default=False),
sa.PrimaryKeyConstraint('id'),
sa.ForeignKeyConstraint(['flavour_uuid'],
['vnf_deployment_flavours.id'], ),
@ -140,7 +148,7 @@ def upgrade(active_plugins=None, options=None):
sa.Column('created_at', sa.DateTime(), nullable=False),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.Column('deleted_at', sa.DateTime(), nullable=True),
sa.Column('deleted', Boolean, default=False),
sa.Column('deleted', deleted_type, default=False),
sa.PrimaryKeyConstraint('id'),
sa.ForeignKeyConstraint(['image_uuid'],
['vnf_software_images.id'], ),

View File

@ -38,7 +38,8 @@ def upgrade(active_plugins=None, options=None):
sa.Column('filter', sa.JSON(), nullable=True),
sa.Column('callbackUri', sa.String(length=255), nullable=False),
sa.Column('authentication', sa.JSON(), nullable=True),
sa.Column('verbosity', sa.Enum('FULL', 'SHORT'), nullable=False),
sa.Column('verbosity',
sa.Enum('FULL', 'SHORT', name='verbosity'), nullable=False),
sa.PrimaryKeyConstraint('id'),
mysql_engine='InnoDB'
)
@ -55,7 +56,8 @@ def upgrade(active_plugins=None, options=None):
sa.Column('vnfConfigurableProperties', sa.JSON(), nullable=True),
sa.Column('vimConnectionInfo', sa.JSON(), nullable=True),
sa.Column('instantiationState',
sa.Enum('NOT_INSTANTIATED', 'INSTANTIATED'),
sa.Enum('NOT_INSTANTIATED', 'INSTANTIATED',
name='instantiationState'),
nullable=False),
sa.Column('instantiatedVnfInfo', sa.JSON(), nullable=True),
sa.Column('metadata', sa.JSON(), nullable=True),
@ -68,7 +70,8 @@ def upgrade(active_plugins=None, options=None):
sa.Column('id', sa.String(length=255), nullable=False),
sa.Column('operationState',
sa.Enum('STARTING', 'PROCESSING', 'COMPLETED', 'FAILED_TEMP',
'FAILED', 'ROLLING_BACK', 'ROLLED_BACK'),
'FAILED', 'ROLLING_BACK', 'ROLLED_BACK',
name='operationState'),
nullable=False),
sa.Column('stateEnteredTime', sa.DateTime(), nullable=False),
sa.Column('startTime', sa.DateTime(), nullable=False),
@ -78,12 +81,15 @@ def upgrade(active_plugins=None, options=None):
sa.Enum('INSTANTIATE', 'SCALE', 'SCALE_TO_LEVEL',
'CHANGE_FLAVOUR', 'TERMINATE', 'HEAL', 'OPERATE',
'CHANGE_EXT_CONN', 'MODIFY_INFO', 'CREATE_SNAPSHOT',
'REVERT_TO_SNAPSHOT', 'CHANGE_VNFPKG'),
'REVERT_TO_SNAPSHOT', 'CHANGE_VNFPKG',
name='operation'),
nullable=False),
sa.Column('isAutomaticInvocation', sa.Boolean(), nullable=False),
sa.Column('operationParams', sa.JSON(), nullable=True),
sa.Column('isCancelPending', sa.Boolean(), nullable=False),
sa.Column('cancelMode', sa.Enum('GRACEFUL', 'FORCEFUL'), nullable=True),
sa.Column('cancelMode',
sa.Enum('GRACEFUL', 'FORCEFUL', name='cancelMode'),
nullable=True),
sa.Column('error', sa.JSON(), nullable=True),
sa.Column('resourceChanges', sa.JSON(), nullable=True),
sa.Column('changedInfo', sa.JSON(), nullable=True),

View File

@ -34,6 +34,78 @@ down_revision = '3adac34764da'
def upgrade(active_plugins=None, options=None):
sql_text_length = 65535
bind = op.get_bind()
engine = bind.engine
if engine.name == 'postgresql':
type = sa.VARCHAR(length=sql_text_length)
operation_states = sa.Computed(
"decode(filter->>'$.operationStates','escape')")
operation_states_len = sa.Computed(
"coalesce(json_array_length(filter->'$.operationStates'),0)")
vnfd_ids = sa.Computed(
"decode(filter->>'$.vnfdIds','escape')")
vnfd_ids_len = sa.Computed(
"coalesce(json_array_length(filter->'$.vnfdIds'),0)")
vnf_provider = sa.Computed(
"(coalesce(decode("
"vnf_products_from_providers->>'$.vnfProvider','escape')),0)")
vnf_product_name = sa.Computed(
"(coalesce(decode("
"vnf_products_from_providers->>"
"'$.vnfProducts[0].vnfProductName','escape')),0)")
vnf_software_version = sa.Computed(
"(coalesce(decode("
"vnf_products_from_providers->>'$.vnfProducts[0]"
".versions[0].vnfSoftwareVersion','escape')),0)")
vnfd_versions = sa.Computed(
"decode(vnf_products_from_providers->>"
"'$.vnfProducts[0].versions[0].vnfdVersions','escape')")
vnfd_versions_len = sa.Computed(
"coalesce(json_array_length(filter->"
"'$.vnfProducts[0].versions[0].vnfdVersions'),0)")
vnf_instance_ids = sa.Computed(
"decode(filter->>'$.vnfInstanceIds','escape')")
vnf_instance_ids_len = sa.Computed(
"coalesce(json_array_length(filter->'$.vnfInstanceIds'),0)")
vnf_instance_names = sa.Computed(
"decode(filter->>'$.vnfInstanceNames','escape')")
vnf_instance_names_len = sa.Computed(
"coalesce(json_array_length(filter->'$.vnfInstanceNames'),0)")
else:
type = sa.TEXT(length=sql_text_length)
operation_states = sa.Computed(
"json_unquote(json_extract(`filter`,'$.operationStates'))")
operation_states_len = sa.Computed(
"ifnull(json_length(`operation_states`),0)")
vnfd_ids = sa.Computed(
"json_unquote(json_extract(`filter`,'$.vnfdIds'))")
vnfd_ids_len = sa.Computed(
"ifnull(json_length(`vnfd_ids`),0)")
vnf_provider = sa.Computed(
"(ifnull(json_unquote(json_extract("
"`vnf_products_from_providers`,'$.vnfProvider')),''))")
vnf_product_name = sa.Computed(
"(ifnull(json_unquote(json_extract("
"`vnf_products_from_providers`,"
"'$.vnfProducts[0].vnfProductName')),''))")
vnf_software_version = sa.Computed(
"(ifnull(json_unquote(json_extract("
"`vnf_products_from_providers`,'$.vnfProducts[0]"
".versions[0].vnfSoftwareVersion')),''))")
vnfd_versions = sa.Computed(
"json_unquote(json_extract(`vnf_products_from_providers`,"
"'$.vnfProducts[0].versions[0].vnfdVersions'))")
vnfd_versions_len = sa.Computed(
"ifnull(json_length(`vnfd_versions`),0)")
vnf_instance_ids = sa.Computed(
"json_unquote(json_extract(`filter`,'$.vnfInstanceIds'))")
vnf_instance_ids_len = sa.Computed(
"ifnull(json_length(`vnf_instance_ids`),0)")
vnf_instance_names = sa.Computed(
"json_unquote(json_extract(`filter`,'$.vnfInstanceNames'))")
vnf_instance_names_len = sa.Computed(
"ifnull(json_length(`vnf_instance_names`),0)")
op.add_column(
'vnf_lcm_filters',
sa.Column(
@ -42,96 +114,64 @@ def upgrade(active_plugins=None, options=None):
op.add_column(
'vnf_lcm_filters',
sa.Column(
'operation_states', sa.TEXT(length=sql_text_length),
sa.Computed(
"json_unquote(json_extract(`filter`,'$.operationStates'))")))
'operation_states', type, operation_states))
op.add_column(
'vnf_lcm_filters',
sa.Column(
'operation_states_len', sa.Integer,
sa.Computed(
"ifnull(json_length(`operation_states`),0)")))
'operation_states_len', sa.Integer, operation_states_len))
op.add_column(
'vnf_lcm_filters',
sa.Column(
'vnfd_ids', sa.TEXT(length=sql_text_length),
sa.Computed(
"json_unquote(json_extract(`filter`,'$.vnfdIds'))")))
'vnfd_ids', type, vnfd_ids))
op.add_column(
'vnf_lcm_filters',
sa.Column(
'vnfd_ids_len', sa.Integer,
sa.Computed(
"ifnull(json_length(`vnfd_ids`),0)")))
'vnfd_ids_len', sa.Integer, vnfd_ids_len))
op.add_column(
'vnf_lcm_filters',
sa.Column(
'vnf_provider', sa.TEXT(length=sql_text_length),
sa.Computed(
"(ifnull(json_unquote(json_extract("
"`vnf_products_from_providers`,'$.vnfProvider')),''))")))
'vnf_provider', type, vnf_provider))
op.add_column(
'vnf_lcm_filters',
sa.Column(
'vnf_product_name', sa.TEXT(length=sql_text_length),
sa.Computed(
"(ifnull(json_unquote(json_extract("
"`vnf_products_from_providers`,"
"'$.vnfProducts[0].vnfProductName')),''))")))
'vnf_product_name', type, vnf_product_name))
op.add_column(
'vnf_lcm_filters',
sa.Column(
'vnf_software_version', sa.TEXT(length=sql_text_length),
sa.Computed(
"(ifnull(json_unquote(json_extract("
"`vnf_products_from_providers`,'$.vnfProducts[0]"
".versions[0].vnfSoftwareVersion')),''))")))
'vnf_software_version', type, vnf_software_version))
op.add_column(
'vnf_lcm_filters',
sa.Column(
'vnfd_versions', sa.TEXT(length=sql_text_length),
sa.Computed(
"json_unquote(json_extract(`vnf_products_from_providers`,"
"'$.vnfProducts[0].versions[0].vnfdVersions'))")))
'vnfd_versions', type, vnfd_versions))
op.add_column(
'vnf_lcm_filters',
sa.Column(
'vnfd_versions_len', sa.Integer,
sa.Computed(
"ifnull(json_length(`vnfd_versions`),0)")))
'vnfd_versions_len', sa.Integer, vnfd_versions_len))
op.add_column(
'vnf_lcm_filters',
sa.Column(
'vnf_instance_ids', sa.TEXT(length=sql_text_length),
sa.Computed(
"json_unquote(json_extract(`filter`,'$.vnfInstanceIds'))")))
'vnf_instance_ids', type, vnf_instance_ids))
op.add_column(
'vnf_lcm_filters',
sa.Column(
'vnf_instance_ids_len', sa.Integer,
sa.Computed(
"ifnull(json_length(`vnf_instance_ids`),0)")))
'vnf_instance_ids_len', sa.Integer, vnf_instance_ids_len))
op.add_column(
'vnf_lcm_filters',
sa.Column(
'vnf_instance_names', sa.TEXT(length=sql_text_length),
sa.Computed(
"json_unquote(json_extract(`filter`,'$.vnfInstanceNames'))")))
'vnf_instance_names', type, vnf_instance_names))
op.add_column(
'vnf_lcm_filters',
sa.Column(
'vnf_instance_names_len', sa.Integer,
sa.Computed(
"ifnull(json_length(`vnf_instance_names`),0)")))
'vnf_instance_names_len', sa.Integer, vnf_instance_names_len))

View File

@ -35,6 +35,26 @@ from tacker.db import types
def upgrade(active_plugins=None, options=None):
bind = op.get_bind()
engine = bind.engine
if engine.name == 'postgresql':
deleted_type = sa.SmallInteger
notif_type = "decode(filter->>'$.notificationTypes','escape')"
notif_type_len = "coalesce \
(json_array_length(filter->'$.notificationTypes'),0)"
op_state = "decode(filter->>'$.operationStates','escape')"
op_state_len = "coalesce \
(json_array_length(filter->'$.operationStates'),0)"
else:
deleted_type = Boolean
notif_type = "json_unquote \
(json_extract('filter','$.notificationTypes'))"
notif_type_len = "ifnull(json_length('notification_types'),0)"
op_state = "json_unquote(json_extract('filter','$.operationStates'))"
op_state_len = "ifnull(json_length('operation_states'),0)"
op.create_table(
'vnf_lcm_subscriptions',
sa.Column('id', types.Uuid(length=36), nullable=False),
@ -43,13 +63,11 @@ def upgrade(active_plugins=None, options=None):
sa.Column('created_at', sa.DateTime(), nullable=False),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.Column('deleted_at', sa.DateTime(), nullable=True),
sa.Column('deleted', Boolean, default=False),
sa.Column('deleted', deleted_type, default=False),
sa.PrimaryKeyConstraint('id'),
mysql_engine='InnoDB'
)
noti_str = "json_unquote(json_extract('filter','$.notificationTypes'))"
sta_str = "json_unquote(json_extract('filter','$.operationStates'))"
op.create_table(
'vnf_lcm_filters',
sa.Column('id', sa.Integer, autoincrement=True, nullable=False),
@ -57,16 +75,16 @@ def upgrade(active_plugins=None, options=None):
sa.Column('filter', sa.JSON(), nullable=False),
sa.Column('notification_types',
sa.LargeBinary(length=65536),
sa.Computed(noti_str)),
sa.Computed(notif_type)),
sa.Column('notification_types_len',
sa.Integer,
sa.Computed("ifnull(json_length('notification_types'),0)")),
sa.Computed(notif_type_len)),
sa.Column('operation_states',
sa.LargeBinary(length=65536),
sa.Computed(sta_str)),
sa.Computed(op_state)),
sa.Column('operation_states_len',
sa.Integer,
sa.Computed("ifnull(json_length('operation_states'),0)")),
sa.Computed(op_state_len)),
sa.PrimaryKeyConstraint('id'),
sa.ForeignKeyConstraint(['subscription_uuid'],
['vnf_lcm_subscriptions.id'], ),
@ -81,10 +99,10 @@ def upgrade(active_plugins=None, options=None):
sa.Column('start_time', sa.DateTime(), nullable=False),
sa.Column('vnf_instance_id', types.Uuid(length=36), nullable=False),
sa.Column('operation', sa.String(length=255), nullable=False),
sa.Column('is_automatic_invocation', sa.Boolean, nullable=False),
sa.Column('is_automatic_invocation', deleted_type, nullable=False),
sa.Column('operation_params', sa.JSON(), nullable=True),
sa.Column('error', sa.JSON(), nullable=True),
sa.Column('deleted', Boolean, default=False),
sa.Column('deleted', deleted_type, default=False),
sa.PrimaryKeyConstraint('id'),
sa.ForeignKeyConstraint(['vnf_instance_id'],
['vnf_instances.id'], ),

View File

@ -32,9 +32,16 @@ import sqlalchemy as sa
def upgrade(active_plugins=None, options=None):
bind = op.get_bind()
engine = bind.engine
if engine.name == 'postgresql':
text_type_maxlen = sa.VARCHAR(length=65535)
else:
text_type_maxlen = sa.TEXT(length=65535)
op.alter_column('ns',
'mgmt_urls', new_column_name='mgmt_ip_addresses',
existing_type=sa.TEXT(65535), nullable=True)
existing_type=text_type_maxlen, nullable=True)
op.alter_column('vnf',
'mgmt_url', new_column_name='mgmt_ip_address',
existing_type=sa.String(255), nullable=True)

View File

@ -35,11 +35,21 @@ from tacker.db import migration
def upgrade(active_plugins=None, options=None):
bind = op.get_bind()
engine = bind.engine
if engine.name == 'postgresql':
deleted_type = sa.SmallInteger
sta_str = "decode(filter->>'$.operationTypes','escape')"
sta_len = "coalesce(json_array_length(filter->'$.operationTypes'),0)"
else:
deleted_type = sa.Boolean
sta_str = "json_unquote(json_extract('filter','$.operationTypes'))"
sta_len = "ifnull(json_length('operation_types'),0)"
op.alter_column('vnf_lcm_filters', 'subscription_uuid',
type_=types.Uuid(length=36), existing_type=sa.String(length=255),
nullable=False)
sta_str = "json_unquote(json_extract('filter','$.operationTypes'))"
op.add_column(
'vnf_lcm_filters',
sa.Column('operation_types',
@ -50,7 +60,7 @@ def upgrade(active_plugins=None, options=None):
'vnf_lcm_filters',
sa.Column('operation_types_len',
sa.Integer,
sa.Computed("ifnull(json_length('operation_types'),0)")))
sa.Computed(sta_len)))
op.drop_column('vnf_lcm_filters', 'operation_states')
op.drop_column('vnf_lcm_filters', 'operation_states_len')
@ -62,7 +72,7 @@ def upgrade(active_plugins=None, options=None):
type_=sa.String(length=16),existing_type=sa.String(length=255))
op.add_column('vnf_lcm_op_occs',
sa.Column('is_cancel_pending', sa.Boolean, nullable=False)),
sa.Column('is_cancel_pending', deleted_type, nullable=False)),
op.add_column('vnf_lcm_op_occs',
sa.Column('resource_changes', sa.JSON(), nullable=True))

View File

@ -42,15 +42,17 @@ def upgrade(active_plugins=None, options=None):
sa.Column('alarmClearedTime', sa.DateTime(), nullable=True),
sa.Column('alarmAcknowledgedTime', sa.DateTime(), nullable=True),
sa.Column('ackState', sa.Enum(
'UNACKNOWLEDGED', 'ACKNOWLEDGED'), nullable=False),
'UNACKNOWLEDGED', 'ACKNOWLEDGED',
name='ackState'), nullable=False),
sa.Column('perceivedSeverity', sa.Enum(
'CRITICAL', 'MAJOR', 'MINOR', 'WARNING',
'INDETERMINATE', 'CLEARED'), nullable=False),
'INDETERMINATE', 'CLEARED',
name='perceivedSeverity'), nullable=False),
sa.Column('eventTime', sa.DateTime(), nullable=False),
sa.Column('eventType', sa.Enum(
'COMMUNICATIONS_ALARM', 'PROCESSING_ERROR_ALARM',
'ENVIRONMENTAL_ALARM', 'QOS_ALARM',
'EQUIPMENT_ALARM'), nullable=False),
'EQUIPMENT_ALARM', name='eventType'), nullable=False),
sa.Column('faultType', sa.String(length=255), nullable=True),
sa.Column('probableCause', sa.String(length=255), nullable=False),
sa.Column('isRootCause', sa.Boolean(), nullable=False),

View File

@ -43,24 +43,71 @@ def upgrade(active_plugins=None, options=None):
# (2) Need to fix SQL statement to utilize sqlalchemy. Currently, we
# use raw SQL with op.exec as a workaround since op.alter_column does
# not work correctly.
alter_sql_notification_types = "ALTER TABLE vnf_lcm_filters CHANGE \
notification_types notification_types text GENERATED \
ALWAYS AS (json_unquote(json_extract(`filter`,\
'$.notificationTypes'))) VIRTUAL;"
bind = op.get_bind()
engine = bind.engine
if engine.name == 'postgresql':
alter_sql_notification_types_drop = "ALTER TABLE vnf_lcm_filters \
DROP COLUMN notification_types;"
alter_sql_notification_types_len = "ALTER TABLE vnf_lcm_filters CHANGE \
notification_types_len notification_types_len int(11) GENERATED \
ALWAYS AS (ifnull(json_length(`notification_types`),0)) VIRTUAL;"
alter_sql_notification_types_add = "ALTER TABLE vnf_lcm_filters ADD \
COLUMN notification_types text GENERATED \
ALWAYS AS (decode(filter->>\
'$.notificationTypes','escape')) STORED;"
alter_sql_operation_types = "ALTER TABLE vnf_lcm_filters CHANGE \
operation_types operation_types text GENERATED ALWAYS AS \
(json_unquote(json_extract(`filter`,'$.operationTypes'))) VIRTUAL;"
alter_sql_notification_types_len_drop = "ALTER TABLE vnf_lcm_filters \
DROP COLUMN notification_types_len;"
alter_sql_operation_types_len = "ALTER TABLE vnf_lcm_filters CHANGE \
operation_types_len operation_types_len int(11) GENERATED ALWAYS \
AS (ifnull(json_length(`operation_types`),0)) VIRTUAL;"
alter_sql_notification_types_len_add = "ALTER TABLE vnf_lcm_filters \
ADD COLUMN notification_types_len numeric(11) GENERATED \
ALWAYS AS (coalesce(json_array_length(filter->\
'$.notificationTypes'),0)) STORED;"
op.execute(alter_sql_notification_types)
op.execute(alter_sql_notification_types_len)
op.execute(alter_sql_operation_types)
op.execute(alter_sql_operation_types_len)
alter_sql_operation_types_drop = "ALTER TABLE vnf_lcm_filters DROP \
COLUMN operation_types;"
alter_sql_operation_types_add = "ALTER TABLE vnf_lcm_filters ADD \
COLUMN operation_types text GENERATED \
ALWAYS AS (decode(filter->>\
'$.operationTypes','escape')) STORED;"
alter_sql_operation_types_len_drop = "ALTER TABLE vnf_lcm_filters \
DROP COLUMN operation_types_len;"
alter_sql_operation_types_len_add = "ALTER TABLE vnf_lcm_filters ADD \
COLUMN operation_types_len numeric(11) GENERATED \
ALWAYS AS (coalesce(json_array_length(filter->\
'$.operationTypes'),0)) STORED;"
op.execute(alter_sql_notification_types_drop)
op.execute(alter_sql_notification_types_add)
op.execute(alter_sql_notification_types_len_drop)
op.execute(alter_sql_notification_types_len_add)
op.execute(alter_sql_operation_types_drop)
op.execute(alter_sql_operation_types_add)
op.execute(alter_sql_operation_types_len_drop)
op.execute(alter_sql_operation_types_len_add)
else:
alter_sql_notification_types = "ALTER TABLE vnf_lcm_filters CHANGE \
notification_types notification_types text GENERATED \
ALWAYS AS (json_unquote(json_extract(`filter`,\
'$.notificationTypes'))) VIRTUAL;"
alter_sql_notification_types_len = "ALTER TABLE vnf_lcm_filters \
CHANGE notification_types_len notification_types_len int(11) \
GENERATED ALWAYS AS \
(ifnull(json_length(`notification_types`),0)) VIRTUAL;"
alter_sql_operation_types = "ALTER TABLE vnf_lcm_filters CHANGE \
operation_types operation_types text GENERATED ALWAYS AS \
(json_unquote(json_extract(`filter`,'$.operationTypes'))) \
VIRTUAL;"
alter_sql_operation_types_len = "ALTER TABLE vnf_lcm_filters CHANGE \
operation_types_len operation_types_len int(11) GENERATED ALWAYS \
AS (ifnull(json_length(`operation_types`),0)) VIRTUAL;"
op.execute(alter_sql_notification_types)
op.execute(alter_sql_notification_types_len)
op.execute(alter_sql_operation_types)
op.execute(alter_sql_operation_types_len)

View File

@ -36,6 +36,14 @@ from tacker.db import types
def upgrade(active_plugins=None, options=None):
bind = op.get_bind()
engine = bind.engine
if engine.name == 'postgresql':
deleted_type = sa.SmallInteger
else:
deleted_type = Boolean
op.create_table(
'vnf_artifacts',
sa.Column('id', types.Uuid(length=36), nullable=False),
@ -47,7 +55,7 @@ def upgrade(active_plugins=None, options=None):
sa.Column('created_at', sa.DateTime(), nullable=False),
sa.Column('updated_at', sa.DateTime(), nullable=True),
sa.Column('deleted_at', sa.DateTime(), nullable=True),
sa.Column('deleted', Boolean, default=False),
sa.Column('deleted', deleted_type, default=False),
sa.PrimaryKeyConstraint('id'),
sa.ForeignKeyConstraint(['package_uuid'],
['vnf_packages.id'], ),

View File

@ -47,9 +47,14 @@ class Json(TypeDecorator):
cache_ok = False
def process_bind_param(self, value, dialect):
return jsonutils.dump_as_bytes(value)
if dialect.name == 'postgresql':
return jsonutils.dumps(value)
else:
return jsonutils.dump_as_bytes(value)
def process_result_value(self, value, dialect):
if value is None:
return None
if isinstance(value, dict):
return value
return jsonutils.loads(value)

View File

@ -200,7 +200,7 @@ class TackerPersistentObject(object):
'created_at': obj_fields.DateTimeField(nullable=False),
'updated_at': obj_fields.DateTimeField(nullable=True),
'deleted_at': obj_fields.DateTimeField(nullable=True),
'deleted': obj_fields.BooleanField(default=False)
'deleted': obj_fields.IntegerField(nullable=True, default=0)
}