Added host_mac, added automatically obtaining mac addresses when needed

This commit is contained in:
Anton Beloglazov 2012-10-16 14:56:11 +11:00
parent 609f701395
commit 90365efb25
2 changed files with 161 additions and 114 deletions

View File

@ -263,7 +263,8 @@ def init_state(config):
'hashed_username': sha1(config['os_admin_user']).hexdigest(),
'hashed_password': sha1(config['os_admin_password']).hexdigest(),
'compute_hosts': common.parse_compute_hosts(
config['compute_hosts'])}
config['compute_hosts']),
'host_macs': {}}
@contract
@ -385,111 +386,6 @@ def execute_underload(config, state, host):
return state
@contract
def flavors_ram(nova):
""" Get a dict of flavor IDs to the RAM limits.
:param nova: A Nova client.
:type nova: *
:return: A dict of flavor IDs to the RAM limits.
:rtype: dict(str: int)
"""
return dict((str(fl.id), fl.ram) for fl in nova.flavors.list())
@contract
def vms_ram_limit(nova, vms):
""" Get the RAM limit from the flavors of the VMs.
:param nova: A Nova client.
:type nova: *
:param vms: A list of VM UUIDs.
:type vms: list(str)
:return: A dict of VM UUIDs to the RAM limits.
:rtype: dict(str: int)
"""
flavors_to_ram = flavors_ram(nova)
vms_ram = {}
for uuid in vms:
try:
vm = nova.servers.get(uuid)
vms_ram[uuid] = flavors_to_ram[vm.flavor['id']]
except novaclient.exceptions.NotFound:
pass
return vms_ram
@contract
def host_used_ram(nova, host):
""" Get the used RAM of the host using the Nova API.
:param nova: A Nova client.
:type nova: *
:param host: A host name.
:type host: str
:return: The used RAM of the host.
:rtype: int
"""
data = nova.hosts.get(host)
if len(data) > 2 and data[2].memory_mb != 0:
return data[2].memory_mb
return data[1].memory_mb
@contract
def vms_by_host(nova, host):
""" Get VMs from the specified host using the Nova API.
:param nova: A Nova client.
:type nova: *
:param host: A host name.
:type host: str
:return: A list of VM UUIDs from the specified host.
:rtype: list(str)
"""
return [str(vm.id) for vm in nova.servers.list()
if vm_hostname(vm) == host]
@contract
def vms_by_hosts(nova, hosts):
""" Get a map of host names to VMs using the Nova API.
:param nova: A Nova client.
:type nova: *
:param hosts: A list of host names.
:type hosts: list(str)
:return: A dict of host names to lists of VM UUIDs.
:rtype: dict(str: list(str))
"""
result = dict((host, []) for host in hosts)
for vm in nova.servers.list():
result[vm_hostname(vm)].append(str(vm.id))
return result
@contract
def vm_hostname(vm):
""" Get the name of the host where VM is running.
:param vm: A Nova VM object.
:type vm: *
:return: The hostname.
:rtype: str
"""
return str(getattr(vm, 'OS-EXT-SRV-ATTR:host'))
@contract
def execute_overload(config, state, vm_uuids):
""" Process an overloaded host: migrate the selected VMs from it.
@ -598,6 +494,133 @@ def execute_overload(config, state, vm_uuids):
return state
@contract
def flavors_ram(nova):
""" Get a dict of flavor IDs to the RAM limits.
:param nova: A Nova client.
:type nova: *
:return: A dict of flavor IDs to the RAM limits.
:rtype: dict(str: int)
"""
return dict((str(fl.id), fl.ram) for fl in nova.flavors.list())
@contract
def vms_ram_limit(nova, vms):
""" Get the RAM limit from the flavors of the VMs.
:param nova: A Nova client.
:type nova: *
:param vms: A list of VM UUIDs.
:type vms: list(str)
:return: A dict of VM UUIDs to the RAM limits.
:rtype: dict(str: int)
"""
flavors_to_ram = flavors_ram(nova)
vms_ram = {}
for uuid in vms:
try:
vm = nova.servers.get(uuid)
vms_ram[uuid] = flavors_to_ram[vm.flavor['id']]
except novaclient.exceptions.NotFound:
pass
return vms_ram
@contract
def host_used_ram(nova, host):
""" Get the used RAM of the host using the Nova API.
:param nova: A Nova client.
:type nova: *
:param host: A host name.
:type host: str
:return: The used RAM of the host.
:rtype: int
"""
data = nova.hosts.get(host)
if len(data) > 2 and data[2].memory_mb != 0:
return data[2].memory_mb
return data[1].memory_mb
@contract
def host_mac(host):
""" Get mac address of a host.
:param host: A host name.
:type host: str
:return: The mac address of the host.
:rtype: str
"""
mac = subprocess.Popen(
("ping -c 1 {0} > /dev/null;" +
"arp -a {0} | awk '{{print $4}}'").format(host),
stdout=subprocess.PIPE,
shell=True).communicate()[0].strip()
if len(mac) != 17:
log.warning('Received a wrong mac address for %s: %s',
host, mac)
return ''
return mac
@contract
def vms_by_host(nova, host):
""" Get VMs from the specified host using the Nova API.
:param nova: A Nova client.
:type nova: *
:param host: A host name.
:type host: str
:return: A list of VM UUIDs from the specified host.
:rtype: list(str)
"""
return [str(vm.id) for vm in nova.servers.list()
if vm_hostname(vm) == host]
@contract
def vms_by_hosts(nova, hosts):
""" Get a map of host names to VMs using the Nova API.
:param nova: A Nova client.
:type nova: *
:param hosts: A list of host names.
:type hosts: list(str)
:return: A dict of host names to lists of VM UUIDs.
:rtype: dict(str: list(str))
"""
result = dict((host, []) for host in hosts)
for vm in nova.servers.list():
result[vm_hostname(vm)].append(str(vm.id))
return result
@contract
def vm_hostname(vm):
""" Get the name of the host where VM is running.
:param vm: A Nova VM object.
:type vm: *
:return: The hostname.
:rtype: str
"""
return str(getattr(vm, 'OS-EXT-SRV-ATTR:host'))
@contract
def migrate_vms(nova, placement):
""" Synchronously live migrate a set of VMs.
@ -686,27 +709,36 @@ def switch_hosts_off(db, sleep_command, hosts):
"""
if sleep_command:
for host in hosts:
command = 'ssh {0} "{1}"'. \
format(host, sleep_command)
if log.isEnabledFor(logging.DEBUG):
log.debug('Calling: ssh {0} "{1}"'. \
format(host, sleep_command))
subprocess.call(
'ssh {0} "{1}"'.format(host, sleep_command),
shell=True)
log.debug('Calling: %s', command)
subprocess.call(command, shell=True)
if log.isEnabledFor(logging.INFO):
log.info('Switched off hosts: %s', str(hosts))
db.insert_host_states(dict((x, 0) for x in hosts))
@contract
def switch_hosts_on(db, hosts):
def switch_hosts_on(db, host_macs, hosts):
""" Switch hosts to the active mode.
:param db: The database object.
:type db: Database
:param host_macs: A dict of host names to mac addresses.
:type host_macs: dict(str: str)
:param hosts: A list of hosts to switch on.
:type hosts: list(str)
"""
for host in hosts:
if host not in host_macs:
host_macs[host] = host_mac(host)
command = 'ether-wake {0}'.format(host_macs[host])
if log.isEnabledFor(logging.DEBUG):
log.debug('Calling: %s', command)
subprocess.call(command, shell=True)
if log.isEnabledFor(logging.INFO):
log.info('Switched on hosts: %s', str(hosts))
db.insert_host_states(dict((x, 1) for x in hosts))

View File

@ -200,6 +200,7 @@ class GlobalManager(TestCase):
assert state['hashed_username'] == sha1('user').hexdigest()
assert state['hashed_password'] == sha1('password').hexdigest()
assert state['compute_hosts'] == hosts
assert state['host_macs'] == {}
def test_service(self):
app = mock('app')
@ -369,9 +370,23 @@ class GlobalManager(TestCase):
manager.switch_hosts_off(db, '', ['h1', 'h2'])
def test_switch_hosts_on(self):
db = db_utils.init_db('sqlite:///:memory:')
with MockTransaction:
db = db_utils.init_db('sqlite:///:memory:')
expect(subprocess).call('ether-wake mac1', shell=True).once()
expect(subprocess).call('ether-wake mac2', shell=True).once()
expect(manager).host_mac('h1').and_return('mac1').once()
expect(db).insert_host_states({
'h1': 1,
'h2': 1}).once()
manager.switch_hosts_on(db, ['h1', 'h2'])
manager.switch_hosts_on(db, {'h2': 'mac2'}, ['h1', 'h2'])
with MockTransaction:
expect(subprocess).call('ether-wake mac1', shell=True).once()
expect(subprocess).call('ether-wake mac2', shell=True).once()
expect(manager).host_mac('h1').and_return('mac1').once()
expect(manager).host_mac('h2').and_return('mac2').once()
expect(db).insert_host_states({
'h1': 1,
'h2': 1}).once()
manager.switch_hosts_on(db, {}, ['h1', 'h2'])