neutron-db-manage以及alembic

这篇文章来看下neutron中的neutron-db-manage工具。在看这个之前先来简单介绍下alembic。

alembic的官网为https://alembic.readthedocs.org/en/latest/,其是一个轻量级的用于SQLAlchemy迁移数据的工具。

alembic需要一个工作目录,目录中有如下的东西:
env.py:用于alembic运行前设置下相关环境。比如建立SQLAlchemy engine、获取连接啥的。
README:一个README文件
script.py.mako:一个用于生成migration scripts的mako文件。
versions:用于存放不同版本的相关脚本。
alembic.ini:用于存放配置信息

建立工作目录很简单:

[root@DEVSTACK01 tmp]# mkdir myproject
[root@DEVSTACK01 tmp]# cd myproject/
[root@DEVSTACK01 myproject]# alembic init alembic
  Creating directory /tmp/myproject/alembic ... done
  Creating directory /tmp/myproject/alembic/versions ... done
  Generating /tmp/myproject/alembic/README ... done
  Generating /tmp/myproject/alembic.ini ... done
  Generating /tmp/myproject/alembic/env.py ... done
  Generating /tmp/myproject/alembic/script.py.mako ... done
  Generating /tmp/myproject/alembic/env.pyc ... done
  Please edit configuration/connection/logging settings in '/tmp/myproject/alembic.ini' before proceeding.

现在来生成我们的script,比如按照官网的例子:

[root@DEVSTACK01 myproject]# alembic revision -m "create account table"
  Generating /tmp/myproject/alembic/versions/ca8bd7f0860_create_account_table.py ... done
[root@DEVSTACK01 myproject]# cat /tmp/myproject/alembic/versions/ca8bd7f0860_create_account_table.py
"""create account table

Revision ID: ca8bd7f0860
Revises: 
Create Date: 2015-05-23 09:17:10.489547

"""

# revision identifiers, used by Alembic.
revision = 'ca8bd7f0860'
down_revision = None
branch_labels = None
depends_on = None

from alembic import op
import sqlalchemy as sa


def upgrade():
    pass


def downgrade():
    pass

可以看到alembic帮我们生成了一个脚本用于创建account table。然后我们就可以在其中添加建表语句了。比如:

def upgrade():
    op.create_table(
        'account',
        sa.Column('id', sa.Integer, primary_key=True),
        sa.Column('name', sa.String(50), nullable=False),
        sa.Column('description', sa.Unicode(200)),
    )

def downgrade():
    op.drop_table('account')

现在我们就能运行如下命令在数据库中建立表了:

[root@DEVSTACK01 myproject]# alembic upgrade head
INFO  [alembic.migration] Context impl MySQLImpl.
INFO  [alembic.migration] Will assume non-transactional DDL.
INFO  [alembic.migration] Running upgrade  -> ca8bd7f0860, create account table
INFO  [alembic.migration] Running upgrade ca8bd7f0860 -> 83f956be02a, create account table

head表明使用最近的一个版本。

这里可能会遇到这个错误:

sqlalchemy.exc.NoSuchModuleError: Can't load plugin: sqlalchemy.dialects:driver

原因是alembic.ini中的链接字符串没有配置,需要根据实际情况配置下:

sqlalchemy.url = mysql://root:rootroot@127.0.0.1/test

现在来一个新版本,比如新增一例,所以按照上面的例子,我们执行:

[root@DEVSTACK01 myproject]# alembic revision -m "Add a column"
  Generating /tmp/myproject/alembic/versions/44397d5272bb_add_a_column.py ... done

然后在生产的文件中写入如下信息:

[root@DEVSTACK01 myproject]# cat /tmp/myproject/alembic/versions/44397d5272bb_add_a_column.py 
"""Add a column

Revision ID: 44397d5272bb
Revises: 83f956be02a
Create Date: 2015-05-23 09:35:52.067706

"""

# revision identifiers, used by Alembic.
revision = '44397d5272bb'
down_revision = '83f956be02a'
branch_labels = None
depends_on = None

from alembic import op
import sqlalchemy as sa

def upgrade():
    op.add_column('account', sa.Column('last_transaction_date', sa.DateTime))

def downgrade():
    op.drop_column('account', 'last_transaction_date')

这里注意下revision,可以看到down_revision是我们上个版本的revision。

此时再执行一次:

[root@DEVSTACK01 myproject]#  alembic upgrade head
INFO  [alembic.migration] Context impl MySQLImpl.
INFO  [alembic.migration] Will assume non-transactional DDL.
INFO  [alembic.migration] Running upgrade 83f956be02a -> 44397d5272bb, Add a column

可以看到我们的DDL生效了。

官网中还有很多根据版本升级、降级的方法,比如:

alembic upgrade ae1
alembic upgrade +2
alembic downgrade -1

ae1是revision的前面几个字符。

可以通过下面的命令查看目前的revison:

[root@DEVSTACK01 myproject]# alembic current
INFO  [alembic.migration] Context impl MySQLImpl.
INFO  [alembic.migration] Will assume non-transactional DDL.
44397d5272bb (head)

下面命令可以查看版本历史:

[root@DEVSTACK01 myproject]# alembic history --verbose
Rev: 44397d5272bb (head)
Parent: 83f956be02a
Path: /tmp/myproject/alembic/versions/44397d5272bb_add_a_column.py

    Add a column
    
    Revision ID: 44397d5272bb
    Revises: 83f956be02a
    Create Date: 2015-05-23 09:35:52.067706

Rev: 83f956be02a
Parent: ca8bd7f0860
Path: /tmp/myproject/alembic/versions/83f956be02a_create_account_table.py

    create account table
    
    Revision ID: 83f956be02a
    Revises: ca8bd7f0860
    Create Date: 2015-05-23 09:19:10.648888

Rev: ca8bd7f0860
Parent: <base>
Path: /tmp/myproject/alembic/versions/ca8bd7f0860_create_account_table.py

    create account table
    
    Revision ID: ca8bd7f0860
    Revises: 
    Create Date: 2015-05-23 09:17:10.489547

另外对于初始状态,其版本叫做None,可以通过如下命令还原到初始状态:

[root@DEVSTACK01 myproject]# alembic downgrade base
INFO  [alembic.migration] Context impl MySQLImpl.
INFO  [alembic.migration] Will assume non-transactional DDL.
INFO  [alembic.migration] Running downgrade 44397d5272bb -> 83f956be02a, Add a column
INFO  [alembic.migration] Running downgrade 83f956be02a -> ca8bd7f0860, create account table
INFO  [alembic.migration] Running downgrade ca8bd7f0860 -> , create account table

alembic是怎么知道数据库的当前版本的呢?很简单,其建立了一张表:

MariaDB [keystone]> use test;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
MariaDB [test]> show tables;
+-----------------+
| Tables_in_test  |
+-----------------+
| account         |
| alembic_version |
+-----------------+
2 rows in set (0.00 sec)

MariaDB [test]> select * from alembic_version;
+--------------+
| version_num  |
+--------------+
| 44397d5272bb |
+--------------+

alembic还可以生成SQL而不直接执行,比如:

[root@DEVSTACK01 myproject]#  alembic upgrade head --sql
INFO  [alembic.migration] Context impl MySQLImpl.
INFO  [alembic.migration] Generating static SQL
INFO  [alembic.migration] Will assume non-transactional DDL.
CREATE TABLE alembic_version (
    version_num VARCHAR(32) NOT NULL
);

INFO  [alembic.migration] Running upgrade  -> ca8bd7f0860, create account table
-- Running upgrade  -> ca8bd7f0860

CREATE TABLE account (
    id INTEGER NOT NULL AUTO_INCREMENT, 
    name VARCHAR(50) NOT NULL, 
    description VARCHAR(200), 
    PRIMARY KEY (id)
);

INSERT INTO alembic_version (version_num) VALUES ('ca8bd7f0860');

INFO  [alembic.migration] Running upgrade ca8bd7f0860 -> 83f956be02a, create account table
-- Running upgrade ca8bd7f0860 -> 83f956be02a

UPDATE alembic_version SET version_num='83f956be02a' WHERE alembic_version.version_num = 'ca8bd7f0860';

INFO  [alembic.migration] Running upgrade 83f956be02a -> 44397d5272bb, Add a column
-- Running upgrade 83f956be02a -> 44397d5272bb

ALTER TABLE account ADD COLUMN last_transaction_date DATETIME;

UPDATE alembic_version SET version_num='44397d5272bb' WHERE alembic_version.version_num = '83f956be02a';

默认生成的SQL是从base开始生成的。

alembic还有一个功能是Auto Generating Migrations。其能根据数据库中表的当前定义以及应用中SQLAlchemy的model做比较,生成新的migration脚本。这里需要提供给alembic的是SQLAlchemy的model定义,需要在env.py中添加如下内容:

# add your model's MetaData object here
# for 'autogenerate' support
# from myapp import mymodel
# target_metadata = mymodel.Base.metadata
from myapp.mymodel import Base
target_metadata = Base.metadata

通过如下的方法可以自动生成migration脚本:

$ alembic revision --autogenerate -m "Added account table"
INFO [alembic.context] Detected added table 'account'
Generating /path/to/foo/alembic/versions/27c6a30d7c24.py...done

不过需要注意的是,alembic不保证一定生成正确的脚本。务必要检查下。

ok,现在来看下neutron-db-manage。入口代码为:

def main():
    CONF(project='neutron')
    config = get_alembic_config()
    config.neutron_config = CONF

    #TODO(gongysh) enable logging
    CONF.command.func(config, CONF.command.name)

这里command的注册的地方为:

def add_command_parsers(subparsers):
    for name in ['current', 'history', 'branches']:
        parser = add_alembic_subparser(subparsers, name)
        parser.set_defaults(func=do_alembic_command)

    help_text = (getattr(alembic_command, 'branches').__doc__ +
                 ' and validate head file')
    parser = subparsers.add_parser('check_migration', help=help_text)
    parser.set_defaults(func=do_check_migration)

    parser = add_alembic_subparser(subparsers, 'upgrade')
    parser.add_argument('--delta', type=int)
    parser.add_argument('--sql', action='store_true')
    parser.add_argument('revision', nargs='?')
    parser.add_argument('--mysql-engine',
                        default='',
                        help='Change MySQL storage engine of current '
                             'existing tables')
    parser.set_defaults(func=do_upgrade)

    parser = subparsers.add_parser('downgrade', help="(No longer supported)")
    parser.add_argument('None', nargs='?', help="Downgrade not supported")
    parser.set_defaults(func=no_downgrade)

    parser = add_alembic_subparser(subparsers, 'stamp')
    parser.add_argument('--sql', action='store_true')
    parser.add_argument('revision')
    parser.set_defaults(func=do_stamp)

    parser = add_alembic_subparser(subparsers, 'revision')
    parser.add_argument('-m', '--message')
    parser.add_argument('--autogenerate', action='store_true')
    parser.add_argument('--sql', action='store_true')
    parser.set_defaults(func=do_revision)


command_opt = cfg.SubCommandOpt('command',
                                title='Command',
                                help=_('Available commands'),
                                handler=add_command_parsers)

CONF.register_cli_opt(command_opt)

可以看到,通过history、current和branches可以看到当前的一些信息。比如:

[stack@DEVSTACK01 neutron]$ neutron-db-manage history
kilo -> 354db87e3225 (head), nsxv_vdr_metadata.py
20c469a5f920 -> kilo, kilo
28a09af858a8 -> 20c469a5f920, add index for port
268fb5e99aa2 -> 28a09af858a8, Initial operations to support basic quotas on prefix space in a subnet pool
034883111f -> 268fb5e99aa2, Initial operations in support of subnet allocation from a pool
20b99fd19d4f -> 034883111f, Remove allow_overlap from subnetpools
589f9237ca0e -> 20b99fd19d4f, Cisco UCS Manager Mechanism Driver
51c54792158e -> 589f9237ca0e, Cisco N1kv ML2 driver tables
1955efc66455 -> 51c54792158e, Initial operations for subnetpools
35a0f3365720 -> 1955efc66455, weight_scheduler
341ee8a4ccb5 -> 35a0f3365720, add port-security in ml2
f15b1fb526dd -> 341ee8a4ccb5, sync with cisco repo
57dd745253a6 -> f15b1fb526dd, Cascade Floating IP Floating Port deletion
2b801560a332 -> 57dd745253a6, nuage_kilo_migrate
2d2a8a565438 -> 2b801560a332, Remove Hyper-V Neutron Plugin
4119216b7365 -> 2d2a8a565438, ML2 hierarchical binding
bebba223288 -> 4119216b7365, Add index on tenant_id column
43763a9618fd -> bebba223288, Add vlan transparent property to network
16cdf118d31d -> 43763a9618fd, add mtu attributes to network
14be42f3d0a5 -> 16cdf118d31d, extra_dhcp_options IPv6 support
26b54cf9024d -> 14be42f3d0a5, Add default security group table
2a1ee2fb59e0 -> 26b54cf9024d, Add index on allocated
41662e32bce2 -> 2a1ee2fb59e0, Add mac_address unique constraint
4dbe243cd84d -> 41662e32bce2, L3 DVR SNAT mapping
38495dc99731 -> 4dbe243cd84d, nsxv
57086602ca0a -> 38495dc99731, ml2_tunnel_endpoints_table
28c0ffb8ebbd -> 57086602ca0a, scrap_nsx_adv_svcs_models
408cfbf6923c -> 28c0ffb8ebbd, remove mlnx plugin
1f71e54a85e7 -> 408cfbf6923c, remove ryu plugin
44621190bc02 -> 1f71e54a85e7, ml2_network_segments models change for multi-segment network.
juno -> 44621190bc02, add_uniqueconstraint_ipavailability_ranges
544673ac99ab -> juno, juno
1680e1f0c4dc -> 544673ac99ab, add router port relationship
3c346828361e -> 1680e1f0c4dc, Remove Cisco Nexus Monolithic Plugin
16a27a58e093 -> 3c346828361e, metering_label_shared
86d6d9776e2b -> 16a27a58e093, ext_l3_ha_mode
236b90af57ab -> 86d6d9776e2b, Cisco APIC Mechanism Driver
58fe87a01143 -> 236b90af57ab, ml2_type_driver_refactor_dynamic_segments
32f3915891fd -> 58fe87a01143, cisco_csr_routing
aae5706a396 -> 32f3915891fd, cisco_apic_driver_update
3b85b693a95f -> aae5706a396, nuage_provider_networks
327ee5fde2c7 -> 3b85b693a95f, Drop unused servicedefinitions and servicetypes tables.
4eba2f05c2f4 -> 327ee5fde2c7, set_innodb_engine
884573acbf1c -> 4eba2f05c2f4, correct Vxlan Endpoint primary key
5589aa32bf80 -> 884573acbf1c, Drop NSX table in favor of the extra_attributes one
31d7f831a591 -> 5589aa32bf80, L3 scheduler additions to support DVR
37f322991f59 -> 31d7f831a591, add constraint for routerid
2026156eab2f -> 37f322991f59, removing_mapping_tables
3927f7f7c456 -> 2026156eab2f, L2 models to support DVR
db_healing -> 3927f7f7c456, L3 extension distributed mode
5446f2a45467 -> db_healing, Include all tables and make migrations unconditional.
2db5203cb7a9 -> 5446f2a45467, set_server_default
10cd28e692e9 -> 2db5203cb7a9, nuage_floatingip
1b837a7125a9 -> 10cd28e692e9, nuage_extraroute
6be312499f9 -> 1b837a7125a9, Cisco APIC Mechanism Driver
d06e871c0d5 -> 6be312499f9, set_not_null_vlan_id_cisco
4eca4a84f08a -> d06e871c0d5, set_admin_state_up_not_null_ml2
33c3db036fe4 -> 4eca4a84f08a, Remove ML2 Cisco Credentials DB
b65aa907aec -> 33c3db036fe4, set_length_of_description_field_metering
1e5dd1d09b22 -> b65aa907aec, set_length_of_protocol_field
54f7549a0e5f -> 1e5dd1d09b22, set_not_null_fields_lb_stats
icehouse -> 54f7549a0e5f, set_not_null_peer_address
5ac1c354a051 -> icehouse, icehouse
538732fa21e1 -> 5ac1c354a051, n1kv segment allocs for cisco n1kv plugin
2447ad0e9585 -> 538732fa21e1, NEC Rename quantum_id to neutron_id
33dd0a9fa487 -> 2447ad0e9585, Add IPv6 Subnet properties
19180cf98af6 -> 33dd0a9fa487, embrane_lbaas_driver
117643811bca -> 19180cf98af6, nsx_gw_devices
81c553f3776c -> 117643811bca, nec: delete old ofc mapping tables
24c7ea5160d7 -> 81c553f3776c, bsn_consistencyhashes
492a106273f8 -> 24c7ea5160d7, Cisco CSR VPNaaS
2eeaf963a447 -> 492a106273f8, Brocade ML2 Mech. Driver
e766b19a3bb -> 2eeaf963a447, floatingip_status
1b2580001654 -> e766b19a3bb, nuage_initial
abc88c33f74f -> 1b2580001654, nsx_sec_group_mapping
3d2585038b95 -> abc88c33f74f, lb stats
157a5d299379 -> 3d2585038b95, VMware NSX rebranding
50d5ba354c23 -> 157a5d299379, ml2 binding:profile
27cc183af192 -> 50d5ba354c23, ml2 binding:vif_details
4ca36cfc898c -> 27cc183af192, ml2_vnic_type
3d3cb89d84ee -> 4ca36cfc898c, nsx_router_mappings
1421183d533f -> 3d3cb89d84ee, nsx_switch_mappings
50e86cb2637a -> 1421183d533f, NSX DHCP/metadata support
1fcfc149aca4 -> 50e86cb2637a, nsx_mappings
e197124d4b9 -> 1fcfc149aca4, Add a unique constraint on (agent_type, host) columns to prevent a race
condition when an agent entry is 'upserted'.
havana -> e197124d4b9, add unique constraint to members
<base> -> havana, havana_initial
[stack@DEVSTACK01 neutron]$ neutron-db-manage current
No handlers could be found for logger "neutron.quota"
INFO  [alembic.migration] Context impl MySQLImpl.
INFO  [alembic.migration] Will assume non-transactional DDL.
354db87e3225 (head)
[stack@DEVSTACK01 neutron]$ neutron-db-manage branches

来看下这几个简单的是怎么实现了。其实很简单,增加子命令的代码为:

def add_alembic_subparser(sub, cmd):
    return sub.add_parser(cmd, help=getattr(alembic_command, cmd).__doc__)

这里的alembic_command就是alembic:

from alembic import command as alembic_command
from alembic import config as alembic_config
from alembic import environment
from alembic import script as alembic_script
from alembic import util as alembic_util

所以对于这几个命令其实就是调用alembic的对应命令。

对于upgrade、downgrade其实也是一样的道理。

现在来看下db下的migration目录的结构。根目录下的alembic.ini、cli.py我们已经知道是做什么的了。还有一个文件是migrate_to_ml2.py,这个文件是用于从老的openvswitch或linux bridge 的core plugin迁移到ml2的。可以看到其并没有使用alembic,而是直接写SQL:

......
    def migrate_tunnels(self, engine, tunnel_type, vxlan_udp_port=None):
        if tunnel_type == p_const.TYPE_GRE:
            engine.execute("""
              INSERT INTO ml2_gre_allocations
                SELECT tunnel_id as gre_id, allocated
                  FROM ovs_tunnel_allocations
                  WHERE allocated = TRUE
            """)
            engine.execute("""
              INSERT INTO ml2_gre_endpoints
                SELECT ip_address
                  FROM ovs_tunnel_endpoints
            """)
        elif tunnel_type == p_const.TYPE_VXLAN:
            if not vxlan_udp_port:
                vxlan_udp_port = p_const.VXLAN_UDP_PORT
            engine.execute("""
              INSERT INTO ml2_vxlan_allocations
                SELECT tunnel_id as vxlan_vni, allocated
                  FROM ovs_tunnel_allocations
                  WHERE allocated = TRUE
            """)
            engine.execute(sa.text("""
              INSERT INTO ml2_vxlan_endpoints
                SELECT ip_address, :udp_port as udp_port
                  FROM ovs_tunnel_endpoints
            """), udp_port=vxlan_udp_port)
        else:
            raise ValueError(_('Unknown tunnel type: %s') % tunnel_type)
......

可以看到大量的insert into XXX select YYY的语句(折让小秦怀念起当年导数据的日子。。。).

ok,根目录下主要就这些文件,按照alembic的目录规则,肯定有个地方要放置我们的scripts。这个目录就在migration/alembic_migrations/versions下面。但是在migration/alembic_migrations下还有很多其他的py文件,这些文件是干嘛的呢?这里小秦猜测是havana需要的。在havana的scripts中可以看到:

def upgrade():
    agent_init_ops.upgrade()
    core_init_ops.upgrade()
    l3_init_ops.upgrade()
    secgroup_init_ops.upgrade()
    portsec_init_ops.upgrade()
    other_extensions_init_ops.upgrade()
    lb_init_ops.upgrade()
    ovs_init_ops.upgrade()
    ml2_init_ops.upgrade()
    firewall_init_ops.upgrade()
    loadbalancer_init_ops.upgrade()
    vpn_init_ops.upgrade()
    metering_init_ops.upgrade()
    brocade_init_ops.upgrade()
    cisco_init_ops.upgrade()
    mlnx_init_ops.upgrade()
    nec_init_ops.upgrade()
    other_plugins_init_ops.upgrade()
    ryu_init_ops.upgrade()
    vmware_init_ops.upgrade()

而havana是我们的base后的第一个版本,这个从history的命令输出可以看到:

......
1421183d533f -> 3d3cb89d84ee, nsx_switch_mappings
50e86cb2637a -> 1421183d533f, NSX DHCP/metadata support
1fcfc149aca4 -> 50e86cb2637a, nsx_mappings
e197124d4b9 -> 1fcfc149aca4, Add a unique constraint on (agent_type, host) columns to prevent a race
condition when an agent entry is 'upserted'.
havana -> e197124d4b9, add unique constraint to members
<base> -> havana, havana_initial

现在来看下db/migration/models目录。根据上面说的,alembic是可以比较数据库和SQLAlchemy的model自动生成scripts的,models下存放了这些model。在env.py中可以看到:

from logging import config as logging_config

from alembic import context
from oslo_config import cfg
from oslo_db.sqlalchemy import session
import sqlalchemy as sa
from sqlalchemy import event

from neutron.db.migration.models import head  # noqa
from neutron.db import model_base


MYSQL_ENGINE = None

# this is the Alembic Config object, which provides
# access to the values within the .ini file in use.
config = context.config
neutron_config = config.neutron_config

# Interpret the config file for Python logging.
# This line sets up loggers basically.
logging_config.fileConfig(config.config_file_name)

# set the target for 'autogenerate' support
target_metadata = model_base.BASEV2.metadata

当其运行的时候,其会from neutron.db.migration.models import head,后者有一长串的import:

from neutron.db import agents_db  # noqa
from neutron.db import agentschedulers_db  # noqa
from neutron.db import allowedaddresspairs_db  # noqa
from neutron.db import dvr_mac_db  # noqa
from neutron.db import external_net_db  # noqa
from neutron.db import extradhcpopt_db  # noqa
from neutron.db import extraroute_db  # noqa
from neutron.db import l3_agentschedulers_db  # noqa
from neutron.db import l3_attrs_db  # noqa
from neutron.db import l3_db  # noqa
from neutron.db import l3_dvrscheduler_db  # noqa
from neutron.db import l3_gwmode_db  # noqa
from neutron.db import l3_hamode_db  # noqa
from neutron.db.metering import metering_db  # noqa
from neutron.db import model_base
from neutron.db import models_v2  # noqa
from neutron.db import portbindings_db  # noqa
from neutron.db import portsecurity_db  # noqa
from neutron.db import quota_db  # noqa
from neutron.db import securitygroups_db  # noqa
from neutron.db import servicetype_db  # noqa
from neutron.plugins.bigswitch.db import consistency_db  # noqa
from neutron.plugins.bigswitch import routerrule_db  # noqa
from neutron.plugins.brocade.db import models as brocade_models  # noqa
from neutron.plugins.cisco.db.l3 import l3_models  # noqa
from neutron.plugins.cisco.db import n1kv_models_v2  # noqa
from neutron.plugins.cisco.db import network_models_v2  # noqa
from neutron.plugins.linuxbridge.db import l2network_models_v2  # noqa
from neutron.plugins.metaplugin import meta_models_v2  # noqa
from neutron.plugins.ml2.drivers.arista import db  # noqa
from neutron.plugins.ml2.drivers.brocade.db import (  # noqa
    models as ml2_brocade_models)
from neutron.plugins.ml2.drivers.cisco.apic import apic_model  # noqa
from neutron.plugins.ml2.drivers.cisco.n1kv import n1kv_models  # noqa
from neutron.plugins.ml2.drivers.cisco.nexus import (  # noqa
    nexus_models_v2 as ml2_nexus_models_v2)
from neutron.plugins.ml2.drivers.cisco.ucsm import ucsm_model  # noqa
from neutron.plugins.ml2.drivers import type_flat  # noqa
from neutron.plugins.ml2.drivers import type_gre  # noqa
from neutron.plugins.ml2.drivers import type_vlan  # noqa
from neutron.plugins.ml2.drivers import type_vxlan  # noqa
from neutron.plugins.ml2 import models  # noqa
from neutron.plugins.nec.db import models as nec_models  # noqa
from neutron.plugins.nuage import nuage_models  # noqa
from neutron.plugins.openvswitch import ovs_models_v2  # noqa
from neutron.plugins.vmware.dbexts import nsx_models  # noqa
from neutron.plugins.vmware.dbexts import nsxv_models  # noqa
from neutron.plugins.vmware.dbexts import vcns_models  # noqa

因此alembic通过这个可以获取到现在table的model,metadata中包含了这些信息。于是alembic就可以自动生成对应的scripts了。

那么这个目录下的frozen.py做了什么呢?从注释可以看到:

"""
This module should not be changed.

The module provides all database models that were present at the moment of
creation of heal_script.

Its purpose is to create comparable metadata with current database schema.
Based on this comparison database can be healed with healing migration.

Current HEAD commit is 59da928e945ec58836d34fd561d30a8a446e2728
"""

搜了下,发现有这么一个版本:

db_healing -> 3927f7f7c456, L3 extension distributed mode
5446f2a45467 -> db_healing, Include all tables and make migrations unconditional.

好吧,看来在某一个版本会强制同步下所有的table。有兴趣的同学可以看下heal_script.py的实现。

ok,通过上面分析可以看到,基本上neutron-db-manager就是简单的封装了下alembic,大部分重要的命令都是直接传递给了alembic来执行的。所以下面是一些常见的命令:

[stack@DEVSTACK01 neutron]$ neutron-db-manage history #查看history
[stack@DEVSTACK01 neutron]$ neutron-db-manage current #查看当前版本
[stack@DEVSTACK01 neutron]$ neutron-db-manage branches #查看branches,neutron目前没有用这个
[stack@DEVSTACK01 neutron]$ neutron-db-manage upgrade head #升级到最新版本

如果我们修改了某个表的结构,比如小秦在routers表上加了个price的列,那么有两种方法生成scripts。一种是执行类似如下命令:

$ neutron-db-manage --config-file /path/to/neutron.conf \
--config-file /path/to/plugin/config.ini revision \
-m "description of revision"

在生成的scripts中加入对应的DDL操作。还有一种是:

$ neutron-db-manage --config-file /path/to/neutron.conf \
--config-file /path/to/plugin/config.ini revision \
-m "description of revision" \
--autogenerate

但如果是新增表,这里需要在head.py中导入新增加表的model所在的module。

2 Responses

  1. neoares 2015年8月26日 / 上午11:52

    看博主的文章,学到很多东西~~~~,非常感谢

  2. KK Yang 2016年12月15日 / 下午2:12

    Nice post,正好昨天需要拓展 Networking-sfc 里面的 DB Column,看完这篇文章后,终于里面 Neutron DB 是怎么管理的了。 Thanks!

发表评论

电子邮件地址不会被公开。 必填项已用*标注

*