From: Evan Broder Date: Tue, 11 Nov 2008 04:41:13 +0000 (-0500) Subject: Eliminate the need for a deep copy of database objects during the X-Git-Url: http://xvm.mit.edu/gitweb/invirt/scripts/prod-migration.git/commitdiff_plain/cdc9c24ada16a10982f1327fa4d536774b71898a Eliminate the need for a deep copy of database objects during the migration with weird SQLAlchemy haxory. This requires the migration script to have its own unique version of the database library svn path=/trunk/scripts/; revision=1611 --- diff --git a/lib/database.py b/lib/database.py new file mode 100644 index 0000000..8b95dd9 --- /dev/null +++ b/lib/database.py @@ -0,0 +1,129 @@ +from invirt.config import structs as config +import sqlalchemy + +from sqlalchemy import * +from sqlalchemy import orm +from sqlalchemy.orm import create_session, relation + +from sqlalchemy.ext.sessioncontext import SessionContext +from sqlalchemy.ext.assignmapper import assign_mapper + +def connect(uri = config.db.uri): + """ Connect to a given database URI""" + global session + + engine = sqlalchemy.create_engine(uri, pool_timeout=5) + meta.bind = engine + + session = Session(bind=engine) + +def clear_cache(): + """Clear sqlalchemy's cache + """ + + session.clear() + +meta = MetaData() +Session = orm.sessionmaker(transactional=False, autoflush=False) + +machine_table = Table('machines', meta, + Column('machine_id', Integer, primary_key=True, nullable=False), + Column('name', String, nullable=False), + Column('description', String, nullable=False), + Column('memory', Integer, nullable=False), + Column('owner', String, nullable=False), + Column('contact', String, nullable=False), + Column('uuid', String, nullable=False), + Column('administrator', String, nullable=False, default=False), + Column('type_id', String, ForeignKey('types.type_id'), nullable=False), + Column('autorestart', Boolean, nullable=False, default=False), + Column('cpus', Integer, nullable=False, default=1), + Column('adminable', Boolean, nullable=False, default=False)) + +nic_table = Table('nics', meta, + Column('machine_id', Integer, ForeignKey('machines.machine_id'), nullable=True), + Column('mac_addr', String, nullable=False, primary_key=True), + Column('ip', String, nullable=False, unique=True), + Column('hostname', String, nullable=True)) + +disk_table = Table('disks', meta, + Column('machine_id', Integer, ForeignKey('machines.machine_id'), nullable=False), + Column('guest_device_name', String, nullable=False), + Column('size', Integer, nullable=False), + PrimaryKeyConstraint('machine_id', 'guest_device_name')) + +types_table = Table('types', meta, + Column('type_id', String, primary_key=True, nullable=False), + Column('description', String, nullable=False), + Column('hvm', Boolean, nullable=False), + Column('apic', Boolean, nullable=False), + Column('acpi', Boolean, nullable=False), + Column('pae', Boolean, nullable=False)) + +mirrors_table = Table('mirrors', meta, + Column('mirror_id', String, primary_key=True, nullable=False), + Column('uri_prefix', String, nullable=False)) + +cdroms_table = Table('cdroms', meta, + Column('cdrom_id', String, primary_key=True, nullable=False), + Column('description', String, nullable=False), + Column('mirror_id', String, ForeignKey('mirrors.mirror_id')), + Column('uri_suffix', String)) + +autoinstalls_table = Table('autoinstalls', meta, + Column('autoinstall_id', String, primary_key=True, nullable=False), + Column('description', String, nullable=False), + Column('type_id', String, ForeignKey('types.type_id'), nullable=False), + Column('distribution', String, nullable=False), + Column('mirror', String, nullable=False)) + +machine_access_table = Table('machine_access', meta, + Column('machine_id', Integer, ForeignKey('machines.machine_id', ondelete='CASCADE'), nullable=False, index=True), + Column('user', String, nullable=False, index=True), + PrimaryKeyConstraint('machine_id', 'user')) + +class Machine(object): + def __repr__(self): + return "" % (self.machine_id, self.name, self.owner) + +class MachineAccess(object): + def __repr__(self): + return "" % (self.machine, self.user) + +class NIC(object): + def __repr__(self): + return "" % (self.mac_addr, self.machine_id, self.ip, self.hostname) + +class Disk(object): + def __repr__(self): + return "" % (self.machine_id, self.guest_device_name, self.size) + +class Type(object): + def __repr__(self): + return "" % (self.type_id, self.description) + +class Mirror(object): + def __repr__(self): + return "" % (self.mirror_id) + +class CDROM(object): + def __repr__(self): + return "" % (self.cdrom_id, self.description) + +class Autoinstall(object): + def __repr__(self): + return "" % (self.autoinstall_id, self.description, self.type.type_id) + +orm.mapper(Machine, machine_table, + properties={'nics': relation(NIC, backref="machine", lazy=False), + 'disks': relation(Disk, backref="machine", lazy=False), + 'type': relation(Type, lazy=False), + 'acl': relation(MachineAccess, backref="machine", lazy=False, passive_deletes=True, cascade="all, delete-orphan")}); +orm.mapper(MachineAccess, machine_access_table) +orm.mapper(NIC, nic_table) +orm.mapper(Disk, disk_table) +orm.mapper(Type, types_table) +orm.mapper(Mirror, mirrors_table) +orm.mapper(CDROM, cdroms_table, + properties={'mirror': relation(Mirror, backref="cdroms")}) +orm.mapper(Autoinstall, autoinstalls_table) diff --git a/xvm-migrate-machine b/xvm-migrate-machine index f14c1b2..a58415a 100644 --- a/xvm-migrate-machine +++ b/xvm-migrate-machine @@ -7,24 +7,36 @@ not_ready_yet_do_not_run_me -from invirt import database +from lib import database dev_db_uri = 'postgres://sipb-xen@sipb-xen-dev.mit.edu/sipb_xen' +database.connect(dev_db_uri) +dev_sess = database.session +database.connect() +prod_sess = database.session def take_data(machine_name): ## dump from dev db; save info well - database.connect(dev_db_uri) - database.session.begin() - machine = database.Machine.query().get_by(name=machine_name) - disks = database.Disk.query().filter_by(machine=machine).all() - nics = database.NIC.query().filter_by(machine=machine).all() -## remove from dev db; ideally atomic with dump + dev_sess.begin() + machine = dev_sess.query(database.Machine).filter_by(name=machine_name).one() + + # Clean out the ACL just so we don't have to think about it + machine.acl = [] + dev_sess.update(machine) + + disks = machine.disks + nics = machine.nics for r in disks + nics + [machine]: database.session.delete(r) + database.session.commit() - return machine, disks, nics - + + for r in disks + nics + [machine]: + dev_sess.expunge(i) + del i._instance_key + + return machine ## shut down if up #remctl remote control $MACHINE destroy @@ -37,12 +49,13 @@ ssh t-i dd if=/dev/xenvg/"$lvname" of=/dev/stdout bs=1M \ | dd if=/dev/stdin of=/dev/xenvg/"$lvname" bs=1M ## add to dev db -def restore_data(machine, disks, nics): - database.connect() # to prod DB - database.session.begin() +def restore_data(machine): + machine.type = prod_sess.query(database.Type).filter_by(type_id=machine.type.type_id).one() + prod_sess.begin() ## now copy machine, disks, nics to new ORM objects (yuck, oh well) ## and database.session.save(those) - database.session.commit() + prod_sess.save(machine) + prod_sess.commit() ## power on if desired