Huge network refactor, Round I

Made network into its own binary
Made simple network a plugabble class
Fixed unittests
Moved various classes around
Moved mac generation into network class
This commit is contained in:
Vishvananda Ishaya
2010-08-03 14:31:47 -07:00
parent c02dc69e72
commit e163a8475d
4 changed files with 146 additions and 115 deletions

View File

@@ -35,32 +35,34 @@ sys.path.append(os.path.abspath(os.path.join(__file__, "../../")))
from nova import flags
from nova import rpc
from nova import utils
from nova.compute import linux_net
from nova.compute import network
from nova.network import linux_net
from nova.network import model
from nova.network import service
FLAGS = flags.FLAGS
def add_lease(mac, ip, hostname, interface):
if FLAGS.fake_rabbit:
network.lease_ip(ip)
service.VlanNetworkService().lease_ip(ip)
else:
rpc.cast(FLAGS.cloud_topic, {"method": "lease_ip",
"args" : {"address": ip}})
rpc.cast("%s.%s" (FLAGS.network_topic, FLAGS.node_name),
{"method": "lease_ip",
"args" : {"fixed_ip": ip}})
def old_lease(mac, ip, hostname, interface):
logging.debug("Adopted old lease or got a change of mac/hostname")
def del_lease(mac, ip, hostname, interface):
if FLAGS.fake_rabbit:
network.release_ip(ip)
service.VlanNetworkService().release_ip(ip)
else:
rpc.cast(FLAGS.cloud_topic, {"method": "release_ip",
"args" : {"address": ip}})
rpc.cast("%s.%s" (FLAGS.network_topic, FLAGS.node_name),
{"method": "release_ip",
"args" : {"fixed_ip": ip}})
def init_leases(interface):
net = network.get_network_by_interface(interface)
net = model.get_network_by_interface(interface)
res = ""
for host_name in net.hosts:
res += "%s\n" % linux_net.hostDHCP(net, host_name, net.hosts[host_name])

View File

@@ -21,12 +21,19 @@
Twistd daemon for the nova network nodes.
"""
from nova import flags
from nova import twistd
from nova.network import service
from nova import utils
FLAGS = flags.FLAGS
flags.DEFINE_string('network_service',
'nova.network.service.VlanNetworkService',
'Service Class for Networking')
if __name__ == '__main__':
twistd.serve(__file__)
if __name__ == '__builtin__':
application = service.NetworkService.create()
application = utils.import_class(FLAGS.network_service).create()

View File

@@ -36,11 +36,9 @@ from nova import utils
from nova.auth import rbac
from nova.auth import manager
from nova.compute import model
from nova.compute import network
from nova.compute.instance_types import INSTANCE_TYPES
from nova.compute import service as compute_service
from nova.endpoint import images
from nova.volume import service as volume_service
from nova.volume import service
FLAGS = flags.FLAGS
@@ -64,7 +62,6 @@ class CloudController(object):
"""
def __init__(self):
self.instdir = model.InstanceDirectory()
self.network = network.PublicNetworkController()
self.setup()
@property
@@ -76,7 +73,7 @@ class CloudController(object):
def volumes(self):
""" returns a list of all volumes """
for volume_id in datastore.Redis.instance().smembers("volumes"):
volume = volume_service.get_volume(volume_id)
volume = service.get_volume(volume_id)
yield volume
def __str__(self):
@@ -222,7 +219,7 @@ class CloudController(object):
callback=_complete)
return d
except users.UserError, e:
except manager.UserError as e:
raise
@rbac.allow('all')
@@ -331,7 +328,7 @@ class CloudController(object):
raise exception.NotFound('Instance %s could not be found' % instance_id)
def _get_volume(self, context, volume_id):
volume = volume_service.get_volume(volume_id)
volume = service.get_volume(volume_id)
if context.user.is_admin() or volume['project_id'] == context.project.id:
return volume
raise exception.NotFound('Volume %s could not be found' % volume_id)
@@ -472,29 +469,34 @@ class CloudController(object):
@rbac.allow('netadmin')
def allocate_address(self, context, **kwargs):
address = self.network.allocate_ip(
context.user.id, context.project.id, 'public')
return defer.succeed({'addressSet': [{'publicIp' : address}]})
alloc_result = rpc.call(self._get_network_host(context),
{"method": "allocate_elastic_ip"})
public_ip = alloc_result['result']
return defer.succeed({'addressSet': [{'publicIp' : public_ip}]})
@rbac.allow('netadmin')
def release_address(self, context, public_ip, **kwargs):
self.network.deallocate_ip(public_ip)
# NOTE(vish): Should we make sure this works?
rpc.cast(self._get_network_host(context),
{"method": "deallocate_elastic_ip",
"args": {"elastic_ip": public_ip}})
return defer.succeed({'releaseResponse': ["Address released."]})
@rbac.allow('netadmin')
def associate_address(self, context, instance_id, **kwargs):
def associate_address(self, context, instance_id, public_ip, **kwargs):
instance = self._get_instance(context, instance_id)
self.network.associate_address(
kwargs['public_ip'],
instance['private_dns_name'],
instance_id)
address = self._get_address(context, public_ip)
rpc.cast(self._get_network_host(context),
{"method": "associate_elastic_ip",
"args": {"elastic_ip": address['public_ip'],
"fixed_ip": instance['private_dns_name'],
"instance_id": instance['instance_id']}})
return defer.succeed({'associateResponse': ["Address associated."]})
@rbac.allow('netadmin')
def disassociate_address(self, context, public_ip, **kwargs):
address = self._get_address(context, public_ip)
self.network.disassociate_address(public_ip)
# TODO - Strip the IP from the instance
return defer.succeed({'disassociateResponse': ["Address disassociated."]})
def release_ip(self, context, private_ip, **kwargs):
@@ -505,7 +507,13 @@ class CloudController(object):
self.network.lease_ip(private_ip)
return defer.succeed({'leaseResponse': ["Address leased."]})
def get_network_host(self, context):
# FIXME(vish): this is temporary until we store net hosts for project
import socket
return socket.gethostname()
@rbac.allow('projectmanager', 'sysadmin')
@defer.inlineCallbacks
def run_instances(self, context, **kwargs):
# make sure user can access the image
# vpn image is private so it doesn't show up on lists
@@ -539,14 +547,25 @@ class CloudController(object):
key_data = key_pair.public_key
# TODO: Get the real security group of launch in here
security_group = "default"
if FLAGS.simple_network:
bridge_name = FLAGS.simple_network_bridge
else:
net = network.BridgedNetwork.get_network_for_project(
context.user.id, context.project.id, security_group)
bridge_name = net['bridge_name']
create_result = yield rpc.call(FLAGS.network_topic,
{"method": "create_network",
"args": {"user_id": context.user.id,
"project_id": context.project.id,
"security_group": security_group}})
bridge_name = create_result['result']
net_host = self._get_network_host(context)
for num in range(int(kwargs['max_count'])):
vpn = False
if image_id == FLAGS.vpn_image_id:
vpn = True
allocate_result = yield rpc.call(net_host,
{"method": "allocate_fixed_ip",
"args": {"user_id": context.user.id,
"project_id": context.project.id,
"vpn": vpn}})
inst = self.instdir.new()
inst['mac_address'] = allocate_result['result']['mac_address']
inst['private_dns_name'] = allocate_result['result']['ip_address']
inst['image_id'] = image_id
inst['kernel_id'] = kernel_id
inst['ramdisk_id'] = ramdisk_id
@@ -558,24 +577,9 @@ class CloudController(object):
inst['key_name'] = kwargs.get('key_name', '')
inst['user_id'] = context.user.id
inst['project_id'] = context.project.id
inst['mac_address'] = utils.generate_mac()
inst['ami_launch_index'] = num
inst['bridge_name'] = bridge_name
if FLAGS.simple_network:
address = network.allocate_simple_ip()
else:
if inst['image_id'] == FLAGS.vpn_image_id:
address = network.allocate_vpn_ip(
inst['user_id'],
inst['project_id'],
mac=inst['mac_address'])
else:
address = network.allocate_ip(
inst['user_id'],
inst['project_id'],
mac=inst['mac_address'])
inst['private_dns_name'] = str(address)
# TODO: allocate expresses on the router node
inst.save()
rpc.cast(FLAGS.compute_topic,
{"method": "run_instance",
@@ -583,8 +587,7 @@ class CloudController(object):
logging.debug("Casting to node for %s's instance with IP of %s" %
(context.user.name, inst['private_dns_name']))
# TODO: Make Network figure out the network name from ip.
return defer.succeed(self._format_instances(
context, reservation_id))
defer.returnValue(self._format_instances(context, reservation_id))
@rbac.allow('projectmanager', 'sysadmin')
def terminate_instances(self, context, instance_id, **kwargs):
@@ -594,26 +597,34 @@ class CloudController(object):
try:
instance = self._get_instance(context, i)
except exception.NotFound:
logging.warning("Instance %s was not found during terminate" % i)
logging.warning("Instance %s was not found during terminate"
% i)
continue
try:
self.network.disassociate_address(
instance.get('public_dns_name', 'bork'))
except:
pass
if instance.get('private_dns_name', None):
logging.debug("Deallocating address %s" % instance.get('private_dns_name', None))
if FLAGS.simple_network:
network.deallocate_simple_ip(instance.get('private_dns_name', None))
else:
try:
self.network.deallocate_ip(instance.get('private_dns_name', None))
except Exception, _err:
pass
if instance.get('node_name', 'unassigned') != 'unassigned': #It's also internal default
elastic_ip = instance.get('public_dns_name', None)
if elastic_ip:
logging.debug("Deallocating address %s" % elastic_ip)
# NOTE(vish): Right now we don't really care if the ip is
# disassociated. We may need to worry about
# checking this later. Perhaps in the scheduler?
rpc.cast(self._get_network_host(context),
{"method": "disassociate_elastic_ip",
"args": {"elastic_ip": elastic_ip}})
fixed_ip = instance.get('private_dns_name', None)
if fixed_ip:
logging.debug("Deallocating address %s" % fixed_ip)
# NOTE(vish): Right now we don't really care if the ip is
# actually removed. We may need to worry about
# checking this later. Perhaps in the scheduler?
rpc.cast(self._get_network_host(context),
{"method": "deallocate_fixed_ip",
"args": {"elastic_ip": elastic_ip}})
if instance.get('node_name', 'unassigned') != 'unassigned':
# NOTE(joshua?): It's also internal default
rpc.cast('%s.%s' % (FLAGS.compute_topic, instance['node_name']),
{"method": "terminate_instance",
"args" : {"instance_id": i}})
{"method": "terminate_instance",
"args": {"instance_id": i}})
else:
instance.destroy()
return defer.succeed(True)

View File

@@ -24,8 +24,9 @@ from nova import flags
from nova import test
from nova import utils
from nova.auth import manager
from nova.compute import network
from nova.compute.exception import NoMoreAddresses
from nova.network import model
from nova.network import service
from nova.network.exception import NoMoreAddresses
FLAGS = flags.FLAGS
@@ -52,7 +53,8 @@ class NetworkTestCase(test.TrialTestCase):
self.projects.append(self.manager.create_project(name,
'netuser',
name))
self.network = network.PublicNetworkController()
self.network = model.PublicNetworkController()
self.service = service.VlanNetworkService()
def tearDown(self):
super(NetworkTestCase, self).tearDown()
@@ -66,16 +68,17 @@ class NetworkTestCase(test.TrialTestCase):
self.assertTrue(IPy.IP(address) in pubnet)
self.assertTrue(IPy.IP(address) in self.network.network)
def test_allocate_deallocate_ip(self):
address = network.allocate_ip(
self.user.id, self.projects[0].id, utils.generate_mac())
def test_allocate_deallocate_fixed_ip(self):
result = self.service.allocate_fixed_ip(
self.user.id, self.projects[0].id)
address = result['ip']
mac = result['mac']
logging.debug("Was allocated %s" % (address))
net = network.get_project_network(self.projects[0].id, "default")
net = model.get_project_network(self.projects[0].id, "default")
self.assertEqual(True, is_in_project(address, self.projects[0].id))
mac = utils.generate_mac()
hostname = "test-host"
self.dnsmasq.issue_ip(mac, address, hostname, net.bridge_name)
rv = network.deallocate_ip(address)
rv = self.service.deallocate_fixed_ip(address)
# Doesn't go away until it's dhcp released
self.assertEqual(True, is_in_project(address, self.projects[0].id))
@@ -84,15 +87,18 @@ class NetworkTestCase(test.TrialTestCase):
self.assertEqual(False, is_in_project(address, self.projects[0].id))
def test_range_allocation(self):
mac = utils.generate_mac()
secondmac = utils.generate_mac()
hostname = "test-host"
address = network.allocate_ip(
self.user.id, self.projects[0].id, mac)
secondaddress = network.allocate_ip(
self.user, self.projects[1].id, secondmac)
net = network.get_project_network(self.projects[0].id, "default")
secondnet = network.get_project_network(self.projects[1].id, "default")
result = self.service.allocate_fixed_ip(
self.user.id, self.projects[0].id)
mac = result['mac']
address = result['ip']
result = self.service.allocate_fixed_ip(
self.user, self.projects[1].id)
secondmac = result['mac']
secondaddress = result['ip']
net = model.get_project_network(self.projects[0].id, "default")
secondnet = model.get_project_network(self.projects[1].id, "default")
self.assertEqual(True, is_in_project(address, self.projects[0].id))
self.assertEqual(True, is_in_project(secondaddress, self.projects[1].id))
@@ -103,46 +109,50 @@ class NetworkTestCase(test.TrialTestCase):
self.dnsmasq.issue_ip(secondmac, secondaddress,
hostname, secondnet.bridge_name)
rv = network.deallocate_ip(address)
rv = self.service.deallocate_fixed_ip(address)
self.dnsmasq.release_ip(mac, address, hostname, net.bridge_name)
self.assertEqual(False, is_in_project(address, self.projects[0].id))
# First address release shouldn't affect the second
self.assertEqual(True, is_in_project(secondaddress, self.projects[1].id))
rv = network.deallocate_ip(secondaddress)
rv = self.service.deallocate_fixed_ip(secondaddress)
self.dnsmasq.release_ip(secondmac, secondaddress,
hostname, secondnet.bridge_name)
self.assertEqual(False, is_in_project(secondaddress, self.projects[1].id))
def test_subnet_edge(self):
secondaddress = network.allocate_ip(self.user.id, self.projects[0].id,
utils.generate_mac())
result = self.service.allocate_fixed_ip(self.user.id,
self.projects[0].id)
firstaddress = result['ip']
hostname = "toomany-hosts"
for i in range(1,5):
project_id = self.projects[i].id
mac = utils.generate_mac()
mac2 = utils.generate_mac()
mac3 = utils.generate_mac()
address = network.allocate_ip(
self.user, project_id, mac)
address2 = network.allocate_ip(
self.user, project_id, mac2)
address3 = network.allocate_ip(
self.user, project_id, mac3)
result = self.service.allocate_fixed_ip(
self.user, project_id)
mac = result['mac']
address = result['ip']
result = self.service.allocate_fixed_ip(
self.user, project_id)
mac2 = result['mac']
address2 = result['ip']
result = self.service.allocate_fixed_ip(
self.user, project_id)
mac3 = result['mac']
address3 = result['ip']
self.assertEqual(False, is_in_project(address, self.projects[0].id))
self.assertEqual(False, is_in_project(address2, self.projects[0].id))
self.assertEqual(False, is_in_project(address3, self.projects[0].id))
rv = network.deallocate_ip(address)
rv = network.deallocate_ip(address2)
rv = network.deallocate_ip(address3)
net = network.get_project_network(project_id, "default")
rv = self.service.deallocate_fixed_ip(address)
rv = self.service.deallocate_fixed_ip(address2)
rv = self.service.deallocate_fixed_ip(address3)
net = model.get_project_network(project_id, "default")
self.dnsmasq.release_ip(mac, address, hostname, net.bridge_name)
self.dnsmasq.release_ip(mac2, address2, hostname, net.bridge_name)
self.dnsmasq.release_ip(mac3, address3, hostname, net.bridge_name)
net = network.get_project_network(self.projects[0].id, "default")
rv = network.deallocate_ip(secondaddress)
self.dnsmasq.release_ip(mac, secondaddress, hostname, net.bridge_name)
net = model.get_project_network(self.projects[0].id, "default")
rv = self.service.deallocate_fixed_ip(firstaddress)
self.dnsmasq.release_ip(mac, firstaddress, hostname, net.bridge_name)
def test_release_before_deallocate(self):
pass
@@ -169,7 +179,7 @@ class NetworkTestCase(test.TrialTestCase):
NUM_RESERVED_VPN_IPS)
usable addresses
"""
net = network.get_project_network(self.projects[0].id, "default")
net = model.get_project_network(self.projects[0].id, "default")
# Determine expected number of available IP addresses
num_static_ips = net.num_static_ips
@@ -183,22 +193,23 @@ class NetworkTestCase(test.TrialTestCase):
macs = {}
addresses = {}
for i in range(0, (num_available_ips - 1)):
macs[i] = utils.generate_mac()
addresses[i] = network.allocate_ip(self.user.id, self.projects[0].id, macs[i])
result = self.service.allocate_fixed_ip(self.user.id, self.projects[0].id)
macs[i] = result['mac']
addresses[i] = result['ip']
self.dnsmasq.issue_ip(macs[i], addresses[i], hostname, net.bridge_name)
self.assertRaises(NoMoreAddresses, network.allocate_ip, self.user.id, self.projects[0].id, utils.generate_mac())
self.assertRaises(NoMoreAddresses, self.service.allocate_fixed_ip, self.user.id, self.projects[0].id)
for i in range(0, (num_available_ips - 1)):
rv = network.deallocate_ip(addresses[i])
rv = self.service.deallocate_fixed_ip(addresses[i])
self.dnsmasq.release_ip(macs[i], addresses[i], hostname, net.bridge_name)
def is_in_project(address, project_id):
return address in network.get_project_network(project_id).list_addresses()
return address in model.get_project_network(project_id).list_addresses()
def _get_project_addresses(project_id):
project_addresses = []
for addr in network.get_project_network(project_id).list_addresses():
for addr in model.get_project_network(project_id).list_addresses():
project_addresses.append(addr)
return project_addresses