NSX-T Migrate: migration script for libvirt

Convert libvirt interface definitions on a KVM host, to NSX managed
vSwitch definitions.

Usage:
python nsx_instance_if_migrate.py
  --username=<username>
  --password=<password>
  --project=<project>
  --auth-url=<keystone auth URL>
  [--project-domain-id=<project domain>]
  [--user-domain-id=<user domain>]
  [--machine-type=<migrated machine type]
  [--nsx-bridge=<NSX managed vSwitch>]

  password: Admin user's password
  keystone auth URL: URL to keystone's authentication service
  project domain: Keystone project domain
  user domain: Keystone user domain
  migrated machine type: Overwrites libvirt's machine type
  NSX managed vSwitch: vSwitch on host, managed by NSX

Change-Id: I9124de1c3448311bbb285251426cd851af54ffb6
This commit is contained in:
Kobi Samoray 2017-05-25 12:12:21 +03:00
parent 65f779ea1d
commit ff6d9e821f

View File

@ -0,0 +1,229 @@
# Copyright 2017 VMware, Inc.
# All Rights Reserved
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import getopt
import libvirt
import logging
import re
import sys
import xml.etree.ElementTree as et
from keystoneauth1 import identity
from keystoneauth1 import session
from neutronclient.v2_0 import client
import nova.conf
CONF = nova.conf.CONF
logging.basicConfig(level=logging.INFO)
LOG = logging.getLogger(__name__)
def usage():
print ("python nsx_instance_if_migrate.py --username=<username> "
"--password=<password> --project=<project> "
"--auth-url=<keystone auth URL> "
"[--project-domain-id=<project domain>] "
"[--user-domain-id=<user domain>] "
"[--machine-type=<migrated machine type] "
"[--nsx-bridge=<NSX managed vSwitch>]\n\n"
"Convert libvirt interface definitions on a KVM host, to NSX "
"managed vSwitch definitions\n\n"
" username: Admin user's username\n"
" password: Admin user's password\n"
" keystone auth URL: URL to keystone's authentication service\n"
" project domain: Keystone project domain\n"
" user domain: Keystone user domain\n"
" migrated machine type: Overwrites libvirt's machine type\n"
" NSX managed vSwitch: vSwitch on host, managed by NSX\n\n")
sys.exit()
def get_opts():
opts = {}
o = []
p = re.compile('^-+')
try:
o, a = getopt.getopt(sys.argv[1:], 'h', ['help',
'username=',
'password=',
'project=',
'project-domain-id=',
'user-domain-id=',
'auth-url=',
'machine-type=',
'nsx-bridge='])
except getopt.GetoptError as err:
LOG.error(err)
usage()
for opt, val in o:
if opt in ('h', 'help'):
usage()
else:
opts[p.sub('', opt)] = val
for mandatory_key in ['username', 'password', 'project', 'auth-url']:
if opts.get(mandatory_key) is None:
LOG.error("%s must be specified!", mandatory_key)
usage()
return opts
def xmltag_text_get(obj, tag_name):
tag_obj = obj.find(tag_name)
if tag_obj is not None:
return tag_obj.text
def xmltag_attr_get(obj, tag, attr):
tag_obj = obj.find(tag)
if tag_obj is not None:
return tag_obj.get(attr)
def xmltag_set(elem, tag, **kwargs):
sub_elem = elem.find(tag)
if sub_elem is None:
sub_elem = et.SubElement(elem, tag)
for attr in kwargs.keys():
sub_elem.set(attr, kwargs.get(attr))
return sub_elem
def iface_migrate(neutron, instance_name, iface, nsx_switch):
iface.set('type', 'bridge')
xmltag_set(iface, 'source', bridge=nsx_switch)
virt_port = xmltag_set(iface, 'virtualport', type='openvswitch')
instance_mac = xmltag_attr_get(iface, 'mac', 'address')
if instance_mac is None:
LOG.error("Couldn't find MAC address for instance %s", instance_name)
return
ports = neutron.list_ports(fields=['id'], mac_address=instance_mac)
if len(ports['ports']) != 1:
LOG.error('For instance %(vm)s, invalid ports received from neutron: '
'%(ports)s', {'vm': instance_name, 'ports': ports})
return
neutron_port_id = ports['ports'][0]['id']
xmltag_set(virt_port, 'parameters', interfaceid=neutron_port_id)
xmltag_set(iface, 'driver', name='qemu')
tap_dev = xmltag_attr_get(iface, 'target', 'dev')
if tap_dev is None:
LOG.error("For instance %(vm)s, couldn't find tap device for "
"interface", instance_name)
# remove script tag if found
script_tag = iface.find('script')
if script_tag is not None:
iface.remove(script_tag)
def is_valid_os_data(libvirt_conn, os_type, os_arch, os_machine):
caps_xml = libvirt_conn.getCapabilities()
caps_root = et.fromstring(caps_xml)
for guest_tag in caps_root.findall('guest'):
if (xmltag_text_get(guest_tag, 'os_type') == os_type
and xmltag_attr_get(guest_tag, 'arch', 'name') == os_arch):
for machine_tag in guest_tag.find('arch').findall('machine'):
if machine_tag.text == os_machine:
return True
return False
def instance_migrate(libvirt_conn, neutron, instance, machine_type,
nsx_switch):
xml = instance.XMLDesc()
root = et.fromstring(xml)
instance_name = xmltag_text_get(root, 'name')
if instance_name is None:
LOG.error("Couldn't find instance name in XML")
return
instance_uuid = xmltag_text_get(root, 'uuid')
if instance_uuid is None:
LOG.error("Couldn't find UUID for instance %s", instance_name)
return
# Validate that os is supported by hypervisor
os_tag = root.find('os')
if os_tag is None:
LOG.error("Couldn't find OS tag for instance %s", instance_name)
return
type_tag = os_tag.find('type')
if not is_valid_os_data(libvirt_conn, type_tag.text, type_tag.get('arch'),
type_tag.get('machine')):
LOG.error("Instance %s OS data is invalid or not supported by "
"hypervisor", instance_name)
return
if machine_type is not None:
type_tag.set('machine', machine_type)
devs = root.find('devices')
ifaces = devs.findall('interface')
for iface in ifaces:
iface_migrate(neutron, instance_name, iface, nsx_switch)
instance.undefine()
libvirt_conn.defineXML(et.tostring(root))
LOG.info('Migrated instance %(vm)s (%(uuid)s) successfully!',
{'vm': instance_name, 'uuid': instance_uuid})
def main():
opts = get_opts()
conn = libvirt.open('qemu:///system')
if conn is None:
LOG.error('Failed to connect to libvirt')
exit(1)
auth = identity.Password(username=opts['username'],
password=opts['password'],
project_name=opts['project'],
project_domain_id=opts.get('project-domain-id',
'default'),
user_domain_id=opts.get('user-domain-id',
'default'),
auth_url=opts['auth-url'])
if auth is None:
LOG.error('Failed to authenticate with keystone')
exit(1)
sess = session.Session(auth=auth)
if sess is None:
LOG.error('Failed to create keystone session')
exit(1)
neutron = client.Client(session=sess)
if neutron is None:
LOG.error('Failed to create neutron session')
exit(1)
instances = conn.listAllDomains()
for instance in instances:
try:
instance_migrate(conn, neutron, instance, opts.get('machine-type'),
opts.get('nsx-bridge', CONF.neutron.ovs_bridge))
except Exception as e:
LOG.error('Failed to migrate instance with exception %s', e)
if __name__ == "__main__":
main()