~ubuntu-branches/ubuntu/saucy/nova/saucy-proposed

« back to all changes in this revision

Viewing changes to nova/db/sqlalchemy/utils.py

  • Committer: Package Import Robot
  • Author(s): Chuck Short, Chuck Short, Adam Gandelman
  • Date: 2013-02-22 09:27:29 UTC
  • mfrom: (1.1.68)
  • Revision ID: package-import@ubuntu.com-20130222092729-nn3gt8rf97uvts77
Tags: 2013.1.g3-0ubuntu1
[ Chuck Short ]
* New usptream release. 
* debian/patches/debian/patches/fix-ubuntu-tests.patch: Refreshed.
* debian/nova-baremetal.logrotate: Fix logfile path.
* debian/control, debian/nova-spiceproxy.{install, logrotate, upstart}:
  Add spice html5 proxy support.
* debian/nova-novncproxy.upstart: Start on runlevel [2345]
* debian/rules: Call testr directly since run_tests.sh -N gives weird return
  value when tests pass.
* debian/pyddist-overrides: Add websockify.
* debian/nova-common.postinst: Removed config file conversion, since
  the option is no longer available. (LP: #1110567)
* debian/control: Add python-pyasn1 as a dependency.
* debian/control: Add python-oslo-config as a dependency.
* debian/control: Suggest sysfsutils, sg3-utils, multipath-tools for fibre
  channel support.

[ Adam Gandelman ]
* debian/control: Fix typo (websocikfy -> websockify).

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# vim: tabstop=4 shiftwidth=4 softtabstop=4
 
2
 
 
3
# Copyright (c) 2013 Boris Pavlovic (boris@pavlovic.me).
 
4
# All Rights Reserved.
 
5
#
 
6
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
 
7
#    not use this file except in compliance with the License. You may obtain
 
8
#    a copy of the License at
 
9
#
 
10
#         http://www.apache.org/licenses/LICENSE-2.0
 
11
#
 
12
#    Unless required by applicable law or agreed to in writing, software
 
13
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 
14
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 
15
#    License for the specific language governing permissions and limitations
 
16
#    under the License.
 
17
 
 
18
from migrate.changeset import UniqueConstraint
 
19
from sqlalchemy.engine import reflection
 
20
from sqlalchemy.ext.compiler import compiles
 
21
from sqlalchemy import func
 
22
from sqlalchemy import MetaData, Table, Column, Index
 
23
from sqlalchemy.sql.expression import UpdateBase, literal_column
 
24
from sqlalchemy.sql import select
 
25
from sqlalchemy.types import NullType
 
26
 
 
27
 
 
28
from nova import exception
 
29
from nova.openstack.common import log as logging
 
30
from nova.openstack.common import timeutils
 
31
 
 
32
 
 
33
LOG = logging.getLogger(__name__)
 
34
 
 
35
 
 
36
class InsertFromSelect(UpdateBase):
 
37
    def __init__(self, table, select):
 
38
        self.table = table
 
39
        self.select = select
 
40
 
 
41
 
 
42
@compiles(InsertFromSelect)
 
43
def visit_insert_from_select(element, compiler, **kw):
 
44
    return "INSERT INTO %s %s" % (
 
45
        compiler.process(element.table, asfrom=True),
 
46
        compiler.process(element.select))
 
47
 
 
48
 
 
49
def _drop_unique_constraint_in_sqlite(migrate_engine, table_name, uc_name,
 
50
                                      **col_name_col_instance):
 
51
    insp = reflection.Inspector.from_engine(migrate_engine)
 
52
    meta = MetaData(bind=migrate_engine)
 
53
 
 
54
    table = Table(table_name, meta, autoload=True)
 
55
    columns = []
 
56
    for column in table.columns:
 
57
        if isinstance(column.type, NullType):
 
58
            try:
 
59
                new_column = col_name_col_instance.get(column.name)
 
60
            except Exception as e:
 
61
                msg = _("Please specify column %s in col_name_col_instance "
 
62
                        "param. It is required because column has unsupported "
 
63
                        "type by sqlite).")
 
64
                raise exception.NovaException(msg % column.name)
 
65
 
 
66
            if not isinstance(new_column, Column):
 
67
                msg = _("col_name_col_instance param has wrong type of "
 
68
                        "column instance for column %s It should be instance "
 
69
                        "of sqlalchemy.Column.")
 
70
                raise exception.NovaException(msg % column.name)
 
71
            columns.append(new_column)
 
72
        else:
 
73
            columns.append(column.copy())
 
74
 
 
75
    constraints = [constraint for constraint in table.constraints
 
76
                    if not constraint.name == uc_name]
 
77
 
 
78
    new_table = Table(table_name + "__tmp__", meta, *(columns + constraints))
 
79
    new_table.create()
 
80
 
 
81
    indexes = []
 
82
    for index in insp.get_indexes(table_name):
 
83
        column_names = [new_table.c[c] for c in index['column_names']]
 
84
        indexes.append(Index(index["name"],
 
85
                             *column_names,
 
86
                             unique=index["unique"]))
 
87
 
 
88
    ins = InsertFromSelect(new_table, table.select())
 
89
    migrate_engine.execute(ins)
 
90
    table.drop()
 
91
 
 
92
    [index.create(migrate_engine) for index in indexes]
 
93
    new_table.rename(table_name)
 
94
 
 
95
 
 
96
def drop_unique_constraint(migrate_engine, table_name, uc_name, *columns,
 
97
                           **col_name_col_instance):
 
98
    """
 
99
    This method drops UC from table and works for mysql, postgresql and sqlite.
 
100
    In mysql and postgresql we are able to use "alter table" constuction. In
 
101
    sqlite is only one way to drop UC:
 
102
        1) Create new table with same columns, indexes and constraints
 
103
           (except one that we want to drop).
 
104
        2) Copy data from old table to new.
 
105
        3) Drop old table.
 
106
        4) Rename new table to the name of old table.
 
107
 
 
108
    :param migrate_engine: sqlalchemy engine
 
109
    :param table_name:     name of table that contains uniq constarint.
 
110
    :param uc_name:        name of uniq constraint that will be dropped.
 
111
    :param columns:        columns that are in uniq constarint.
 
112
    :param col_name_col_instance:   contains pair column_name=column_instance.
 
113
                            column_instance is instance of Column. These params
 
114
                            are required only for columns that have unsupported
 
115
                            types by sqlite. For example BigInteger.
 
116
    """
 
117
    if migrate_engine.name in ["mysql", "postgresql"]:
 
118
        meta = MetaData()
 
119
        meta.bind = migrate_engine
 
120
        t = Table(table_name, meta, autoload=True)
 
121
        uc = UniqueConstraint(*columns, table=t, name=uc_name)
 
122
        uc.drop()
 
123
    else:
 
124
        _drop_unique_constraint_in_sqlite(migrate_engine, table_name, uc_name,
 
125
                                          **col_name_col_instance)
 
126
 
 
127
 
 
128
def drop_old_duplicate_entries_from_table(migrate_engine, table_name,
 
129
                                          use_soft_delete, *uc_column_names):
 
130
    """
 
131
    This method is used to drop all old rows that have the same values for
 
132
    columns in uc_columns.
 
133
    """
 
134
    meta = MetaData()
 
135
    meta.bind = migrate_engine
 
136
 
 
137
    table = Table(table_name, meta, autoload=True)
 
138
    columns_for_group_by = [table.c[name] for name in uc_column_names]
 
139
 
 
140
    columns_for_select = [func.max(table.c.id)]
 
141
    columns_for_select.extend(list(columns_for_group_by))
 
142
 
 
143
    duplicated_rows_select = select(columns_for_select,
 
144
                                    group_by=columns_for_group_by,
 
145
                                    having=func.count(table.c.id) > 1)
 
146
 
 
147
    for row in migrate_engine.execute(duplicated_rows_select):
 
148
        # NOTE(boris-42): Do not remove row that has the biggest ID.
 
149
        delete_condition = table.c.id != row[0]
 
150
        for name in uc_column_names:
 
151
            delete_condition &= table.c[name] == row[name]
 
152
 
 
153
        rows_to_delete_select = select([table.c.id]).where(delete_condition)
 
154
        for row in migrate_engine.execute(rows_to_delete_select).fetchall():
 
155
            LOG.info(_("Deleted duplicated row with id: %(id)s from table: "
 
156
                       "%(table)s") % dict(id=row[0], table=table_name))
 
157
 
 
158
        if use_soft_delete:
 
159
            delete_statement = table.update().\
 
160
                    where(delete_condition).\
 
161
                    values({
 
162
                        'deleted': literal_column('id'),
 
163
                        'updated_at': literal_column('updated_at'),
 
164
                        'deleted_at': timeutils.utcnow()
 
165
                    })
 
166
        else:
 
167
            delete_statement = table.delete().where(delete_condition)
 
168
        migrate_engine.execute(delete_statement)