diff --git a/igvm/commands.py b/igvm/commands.py index 569aa3c8..a880a71e 100644 --- a/igvm/commands.py +++ b/igvm/commands.py @@ -295,8 +295,11 @@ def change_address( ) new_address = ip_address(new_address) + new_address_attr = 'ipv6' + if new_address.version == 4: + new_address_attr = 'ipv4' - if vm.dataset_obj['intern_ip'] == new_address: + if vm.dataset_obj[new_address_attr] == new_address: raise ConfigError('New IP address is the same as the old one!') if not vm.hypervisor.get_vlan_network(new_address) and not migrate: @@ -859,7 +862,8 @@ def host_info(vm_hostname): 'status', )), ('Network', ( - 'intern_ip', + 'ipv4', + 'ipv6', 'mac_address', )), ('Resources', ( diff --git a/igvm/drbd.py b/igvm/drbd.py index 7cc81651..495e2bf2 100644 --- a/igvm/drbd.py +++ b/igvm/drbd.py @@ -185,7 +185,7 @@ def get_host_config(self): ' }}' .format( host=self.hv.dataset_obj['hostname'], - addr=self.hv.dataset_obj['intern_ip'], + addr=self.hv.ip_address, port=self.get_device_port(), dm_minor=self.get_device_minor(), lv_name=self.lv_name, diff --git a/igvm/host.py b/igvm/host.py index 27f99dd3..3ff68508 100644 --- a/igvm/host.py +++ b/igvm/host.py @@ -184,3 +184,10 @@ def set_block_size(self, device, bs_kib): .format(bs_kib, device) ) self.run('sync') + + @property + def ip_address(self): + ipaddr = self.dataset_obj.get('ipv6') or self.dataset_obj.get('ipv4') + if not ipaddr: + raise ValueError('This host has no IP address!') + return ipaddr diff --git a/igvm/hypervisor.py b/igvm/hypervisor.py index bbcb3726..71704fb3 100644 --- a/igvm/hypervisor.py +++ b/igvm/hypervisor.py @@ -212,7 +212,7 @@ def check_vm(self, vm, offline): ) # Proper VLAN? - if not self.get_vlan_network(vm.dataset_obj['intern_ip']): + if not self.get_vlan_network(vm.ip_address): raise HypervisorError( 'Hypervisor "{}" does not support route_network "{}".' .format(self.fqdn, vm.route_network) diff --git a/igvm/kvm.py b/igvm/kvm.py index b66a18b5..31929784 100644 --- a/igvm/kvm.py +++ b/igvm/kvm.py @@ -388,7 +388,7 @@ def generate_domain_xml(hypervisor, vm): # VM, instead the VM is updated to the latest settings. # Every KVM setting should be configurable via Serveradmin anyway. props = DomainProperties(hypervisor, vm) - vlan_network = hypervisor.get_vlan_network(vm.dataset_obj['intern_ip']) + vlan_network = hypervisor.get_vlan_network(vm.ip_address) config = { 'name': vm.uid_name, diff --git a/igvm/settings.py b/igvm/settings.py index 1e480dec..09fc0a0f 100644 --- a/igvm/settings.py +++ b/igvm/settings.py @@ -119,31 +119,34 @@ IMAGE_PATH = '/tmp' +HOST_ATTRIBUTES = [ + 'environment', + 'hostname', + 'igvm_locked', + 'ipv4', + 'ipv6', + 'os', + 'state', +] + NETWORK_ATTRIBUTES = [ 'hostname', 'service_groups', ] -HYPERVISOR_ATTRIBUTES = [ +HYPERVISOR_ATTRIBUTES = HOST_ATTRIBUTES + [ 'ceph_disks', 'cpu_perffactor', 'cpu_util_pct', - 'environment', 'hardware_model', - 'hostname', - 'igvm_locked', 'igvm_migration_log', - 'intern_ip', 'iops_avg', - 'igvm_migration_log', 'libvirt_memory_total_gib', 'libvirt_memory_used_gib', 'libvirt_pool_total_gib', 'libvirt_pool_used_gib', 'num_cpu', - 'os', {'route_network': NETWORK_ATTRIBUTES}, - 'state', { 'vlan_networks': [ 'hostname', @@ -170,7 +173,7 @@ }, ] -VM_ATTRIBUTES = [ +VM_ATTRIBUTES = HOST_ATTRIBUTES + [ 'aws_image_id', 'aws_instance_id', 'aws_instance_type', @@ -181,22 +184,16 @@ 'datacenter', 'datacenter_type', 'disk_size_gib', - 'environment', 'function', 'game_market', 'game_type', 'game_world', - 'hostname', - 'igvm_locked', - 'intern_ip', 'io_weight', 'libvirt_pool_override', 'load_99', 'mac', 'memory', 'num_cpu', - 'os', - 'primary_ip6', 'project', {'project_network': NETWORK_ATTRIBUTES}, 'puppet_ca', @@ -206,7 +203,6 @@ 'served_game', 'service_groups', 'sshfp', - 'state', {'hypervisor': HYPERVISOR_ATTRIBUTES}, ] diff --git a/igvm/vm.py b/igvm/vm.py index 2bcd6da8..b882b7fa 100644 --- a/igvm/vm.py +++ b/igvm/vm.py @@ -363,7 +363,7 @@ def start(self, force_stop_failed=True, transaction=None): raise VMError('VM did not come online in time') host_up = wait_until( - str(self.dataset_obj['intern_ip']), + str(self.ip_address), waitmsg='Waiting for SSH to respond', ) if not host_up and force_stop_failed: @@ -408,7 +408,7 @@ def aws_start(self): raise VMError(e) host_up = wait_until( - str(self.dataset_obj['intern_ip']), + str(self.ip_address), waitmsg='Waiting for SSH to respond', ) @@ -431,7 +431,7 @@ def aws_restart(self): raise VMError(e) host_up = wait_until( - str(self.dataset_obj['intern_ip']), + str(self.ip_address), waitmsg='Waiting for SSH to respond', timeout=180 ) @@ -647,7 +647,8 @@ def disk_free(self): def info(self): result = { 'hypervisor': self.hypervisor.fqdn, - 'intern_ip': self.dataset_obj['intern_ip'], + 'ipv4': self.dataset_obj['ipv4'], + 'ipv6': self.dataset_obj['ipv6'], 'num_cpu': self.dataset_obj['num_cpu'], 'memory': self.dataset_obj['memory'], 'disk_size_gib': self.dataset_obj['disk_size_gib'], @@ -807,8 +808,8 @@ def aws_build(self, self.dataset_obj['aws_placement'] ) }, - PrivateIpAddress=str(self.dataset_obj['intern_ip']), - Ipv6Addresses=[{'Ipv6Address':str(self.dataset_obj['primary_ip6'])}], + PrivateIpAddress=str(self.dataset_obj['ipv4']), + Ipv6Addresses=[{'Ipv6Address':str(self.dataset_obj['ipv6'])}], UserData='' if postboot is None else postboot, TagSpecifications=[ { @@ -1045,16 +1046,19 @@ def copy_postboot_script(self, script): self.put('/buildvm-postboot', script, '0755') def restore_address(self): - self.dataset_obj['intern_ip'] = self.old_address + self.dataset_obj['ipv4'] = self.old_address_ipv4 + self.dataset_obj['ipv6'] = self.old_address_ipv6 self.dataset_obj.commit() self.route_network = self.old_network - def change_address(self, new_address, new_network, transaction=None): + def change_address(self, new_address_ipv4, new_address_ipv6, new_network, transaction=None): # All queries to Serveradmin are kept in commands.py. # That's why this metod receives both new address and new network. - self.old_address = self.dataset_obj['intern_ip'] + self.old_address_ipv4 = self.dataset_obj['ipv4'] + self.old_address_ipv6 = self.dataset_obj['ipv6'] self.old_network = self.route_network - self.dataset_obj['intern_ip'] = new_address + self.dataset_obj['ipv4'] = new_address_ipv4 + self.dataset_obj['ipv6'] = new_address_ipv6 self.dataset_obj.commit() self.route_network = new_network diff --git a/tests/test_integration.py b/tests/test_integration.py index 5476cd5d..fd3184c4 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -11,7 +11,7 @@ from adminapi import api from adminapi.dataset import Query -from adminapi.filters import Any +from adminapi.filters import Any, Not from fabric.api import env from fabric.network import disconnect_all from mock import patch @@ -731,12 +731,28 @@ def test_new_address(self): # We don't have a way to ask for new IP address from Serveradmin # and lock it for us. The method below will usually work fine. # When it starts failing, we must develop retry method. - new_address = get_next_address(VM_NET, 2, 'ipv4') + + # First figure out if the network in which the tests are run + # is IPv6-only or dual-stack. For dual-stack networks we must + # use IPv4 and IPv6 will be auto-assigned. + ip_attr = 'ipv4' + test_net = Query( + { + 'servertype': 'route_network', + 'state': Not('retired'), + 'hostname': VM_NET, + }, + ['hostname', 'ipv4', 'ipv6'], + ).get() + if not test_net['ipv4']: + ip_attr = 'ipv6' + + new_address = get_next_address(VM_NET, 2, ip_attr) change_address(VM_HOSTNAME, new_address, offline=True) - obj = Query({'hostname': VM_HOSTNAME}, ['intern_ip']).get() - self.assertEqual(obj['intern_ip'], new_address) + obj = Query({'hostname': VM_HOSTNAME}, [ip_attr]).get() + self.assertEqual(obj[ip_attr], new_address) with _get_vm(VM_HOSTNAME) as vm: vm.run(cmd('ip a | grep {}', new_address)) self.check_vm_present()