From 08d60702f9995f9e756a16c733f6a26b9d0f5019 Mon Sep 17 00:00:00 2001 From: Dan Wendlandt Date: Fri, 22 Jul 2011 16:56:00 -0700 Subject: merge --- nova/network/manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/network/manager.py b/nova/network/manager.py index 005fa73e7..4706da6ea 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -879,7 +879,6 @@ class VlanManager(RPCAllocateFixedIP, FloatingIP, NetworkManager): def _setup_network(self, context, network_ref): """Sets up network on this host.""" - network_ref['dhcp_server'] = self._get_dhcp_ip(context, network_ref) if not network_ref['vpn_public_address']: net = {} address = FLAGS.vpn_ip @@ -887,6 +886,7 @@ class VlanManager(RPCAllocateFixedIP, FloatingIP, NetworkManager): network_ref = db.network_update(context, network_ref['id'], net) else: address = network_ref['vpn_public_address'] + network_ref['dhcp_server'] = self._get_dhcp_ip(context, network_ref) self.driver.ensure_vlan_bridge(network_ref['vlan'], network_ref['bridge'], network_ref['bridge_interface'], -- cgit From 873aad92944f8840e772d65eda4b3320d65a9ce7 Mon Sep 17 00:00:00 2001 From: Dan Wendlandt Date: Mon, 1 Aug 2011 18:11:15 -0700 Subject: initial commit of vif-plugging for network-service interfaces --- bin/nova-dhcpbridge | 24 +++--- nova/network/linux_net.py | 205 ++++++++++++++++++++++++++++++++-------------- nova/network/manager.py | 28 ++----- nova/utils.py | 9 ++ nova/virt/libvirt/vif.py | 2 +- 5 files changed, 174 insertions(+), 94 deletions(-) diff --git a/bin/nova-dhcpbridge b/bin/nova-dhcpbridge index 325642d52..1727ebf9b 100755 --- a/bin/nova-dhcpbridge +++ b/bin/nova-dhcpbridge @@ -48,12 +48,11 @@ flags.DECLARE('auth_driver', 'nova.auth.manager') flags.DECLARE('network_size', 'nova.network.manager') flags.DECLARE('num_networks', 'nova.network.manager') flags.DECLARE('update_dhcp_on_disassociate', 'nova.network.manager') -flags.DEFINE_string('dnsmasq_interface', 'br0', 'Default Dnsmasq interface') LOG = logging.getLogger('nova.dhcpbridge') -def add_lease(mac, ip_address, _hostname, _interface): +def add_lease(mac, ip_address, _hostname): """Set the IP that was assigned by the DHCP server.""" if FLAGS.fake_rabbit: LOG.debug(_("leasing ip")) @@ -67,13 +66,13 @@ def add_lease(mac, ip_address, _hostname, _interface): "args": {"address": ip_address}}) -def old_lease(mac, ip_address, hostname, interface): +def old_lease(mac, ip_address, hostname): """Update just as add lease.""" LOG.debug(_("Adopted old lease or got a change of mac/hostname")) - add_lease(mac, ip_address, hostname, interface) + add_lease(mac, ip_address, hostname) -def del_lease(mac, ip_address, _hostname, _interface): +def del_lease(mac, ip_address, _hostname): """Called when a lease expires.""" if FLAGS.fake_rabbit: LOG.debug(_("releasing ip")) @@ -87,10 +86,10 @@ def del_lease(mac, ip_address, _hostname, _interface): "args": {"address": ip_address}}) -def init_leases(interface): - """Get the list of hosts for an interface.""" +def init_leases(network_id): + """Get the list of hosts for a network.""" ctxt = context.get_admin_context() - network_ref = db.network_get_by_bridge(ctxt, interface) + network_ref = db.network_get(ctxt, network_id) return linux_net.get_dhcp_leases(ctxt, network_ref) @@ -101,7 +100,8 @@ def main(): argv = FLAGS(sys.argv) logging.setup() # check ENV first so we don't break any older deploys - interface = os.environ.get('DNSMASQ_INTERFACE', FLAGS.dnsmasq_interface) + network_id = int(os.environ.get('NETWORK_ID')) + if int(os.environ.get('TESTING', '0')): from nova.tests import fake_flags @@ -117,11 +117,11 @@ def main(): ip = argv[3] hostname = argv[4] msg = _("Called %(action)s for mac %(mac)s with ip %(ip)s and" - " hostname %(hostname)s on interface %(interface)s") % locals() + " hostname %(hostname)s on network %(network_id)s") % locals() LOG.debug(msg) - globals()[action + '_lease'](mac, ip, hostname, interface) + globals()[action + '_lease'](mac, ip, hostname) else: - print init_leases(interface) + print init_leases(network_id) if __name__ == "__main__": main() diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index 8ace07884..ee0ef0b85 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -63,6 +63,11 @@ flags.DEFINE_string('dmz_cidr', '10.128.0.0/24', 'dmz range that should be accepted') flags.DEFINE_string('dnsmasq_config_file', "", 'Override the default dnsmasq settings with this file') +flags.DEFINE_string('linuxnet_interface_driver', + 'nova.network.linux_net.LinuxBridgeInterfaceDriver', + 'Driver used to create ethernet devices.') +flags.DEFINE_string('linuxnet_ovs_integration_bridge', + 'br-int', 'Name of Open vSwitch bridge used with linuxnet') binary_name = os.path.basename(inspect.stack()[-1][1]) @@ -413,7 +418,7 @@ def ensure_metadata_ip(): 'scope', 'link', 'dev', 'lo', check_exit_code=False) -def ensure_vlan_forward(public_ip, port, private_ip): +def ensure_vpn_forward(public_ip, port, private_ip): """Sets up forwarding rules for vlan.""" iptables_manager.ipv4['filter'].add_rule('FORWARD', '-d %s -p udp ' @@ -491,32 +496,11 @@ def ensure_bridge(bridge, interface, net_attrs=None): _execute('sudo', 'brctl', 'setfd', bridge, 0) # _execute('sudo brctl setageing %s 10' % bridge) _execute('sudo', 'brctl', 'stp', bridge, 'off') - _execute('sudo', 'ip', 'link', 'set', bridge, 'up') - if net_attrs: - # NOTE(vish): The ip for dnsmasq has to be the first address on the - # bridge for it to respond to reqests properly - suffix = net_attrs['cidr'].rpartition('/')[2] - out, err = _execute('sudo', 'ip', 'addr', 'add', - '%s/%s' % - (net_attrs['dhcp_server'], suffix), - 'brd', - net_attrs['broadcast'], - 'dev', - bridge, - check_exit_code=False) - if err and err != 'RTNETLINK answers: File exists\n': - raise exception.Error('Failed to add ip: %s' % err) - if(FLAGS.use_ipv6): - _execute('sudo', 'ip', '-f', 'inet6', 'addr', - 'change', net_attrs['cidr_v6'], - 'dev', bridge) - # NOTE(vish): If the public interface is the same as the - # bridge, then the bridge has to be in promiscuous - # to forward packets properly. - if(FLAGS.public_interface == bridge): - _execute('sudo', 'ip', 'link', 'set', - 'dev', bridge, 'promisc', 'on') + if interface: + out, err = _execute('sudo', 'brctl', 'addif', bridge, interface, + check_exit_code=False) + # NOTE(vish): This will break if there is already an ip on the # interface, so we move any ips to the bridge gateway = None @@ -526,9 +510,9 @@ def ensure_bridge(bridge, interface, net_attrs=None): if fields and fields[0] == '0.0.0.0' and fields[-1] == interface: gateway = fields[1] _execute('sudo', 'route', 'del', 'default', 'gw', gateway, - 'dev', interface, check_exit_code=False) + 'dev', interface, check_exit_code=False) out, err = _execute('sudo', 'ip', 'addr', 'show', 'dev', interface, - 'scope', 'global') + 'scope', 'global') for line in out.split('\n'): fields = line.split() if fields and fields[0] == 'inet': @@ -537,8 +521,6 @@ def ensure_bridge(bridge, interface, net_attrs=None): _execute(*_ip_bridge_cmd('add', params, bridge)) if gateway: _execute('sudo', 'route', 'add', 'default', 'gw', gateway) - out, err = _execute('sudo', 'brctl', 'addif', bridge, interface, - check_exit_code=False) if (err and err != "device %s is already a member of a bridge; can't " "enslave it to bridge %s.\n" % (interface, bridge)): @@ -552,6 +534,36 @@ def ensure_bridge(bridge, interface, net_attrs=None): bridge) +def initialize_gateway_device(dev, network_ref): + if not network_ref: + return + + # NOTE(vish): The ip for dnsmasq has to be the first address on the + # bridge for it to respond to reqests properly + suffix = network_ref['cidr'].rpartition('/')[2] + out, err = _execute('sudo', 'ip', 'addr', 'add', + '%s/%s' % + (network_ref['dhcp_server'], suffix), + 'brd', + network_ref['broadcast'], + 'dev', + dev, + check_exit_code=False) + if err and err != 'RTNETLINK answers: File exists\n': + raise exception.Error('Failed to add ip: %s' % err) + if(FLAGS.use_ipv6): + _execute('sudo', 'ip', '-f', 'inet6', 'addr', + 'change', network_ref['cidr_v6'], + 'dev', dev) + # NOTE(vish): If the public interface is the same as the + # bridge, then the bridge has to be in promiscuous + # to forward packets properly. + if(FLAGS.public_interface == dev): + _execute('sudo', 'ip', 'link', 'set', + 'dev', dev, 'promisc', 'on') + _execute('sudo', 'ip', 'link', 'set', dev, 'up') + + def get_dhcp_leases(context, network_ref): """Return a network's hosts config in dnsmasq leasefile format.""" hosts = [] @@ -580,21 +592,21 @@ def get_dhcp_hosts(context, network_ref): # configuration options (like dchp-range, vlan, ...) # aren't reloaded. @utils.synchronized('dnsmasq_start') -def update_dhcp(context, network_ref): +def update_dhcp(context, dev, network_ref): """(Re)starts a dnsmasq server for a given network. If a dnsmasq instance is already running then send a HUP signal causing it to reload, otherwise spawn a new instance. """ - conffile = _dhcp_file(network_ref['bridge'], 'conf') + conffile = _dhcp_file(dev, 'conf') with open(conffile, 'w') as f: f.write(get_dhcp_hosts(context, network_ref)) # Make sure dnsmasq can actually read it (it setuid()s to "nobody") os.chmod(conffile, 0644) - pid = _dnsmasq_pid_for(network_ref['bridge']) + pid = _dnsmasq_pid_for(dev) # if dnsmasq is already running, then tell it to reload if pid: @@ -609,16 +621,16 @@ def update_dhcp(context, network_ref): else: LOG.debug(_('Pid %d is stale, relaunching dnsmasq'), pid) - # FLAGFILE and DNSMASQ_INTERFACE in env + # FLAGFILE and NETWORK_ID in env env = {'FLAGFILE': FLAGS.dhcpbridge_flagfile, - 'DNSMASQ_INTERFACE': network_ref['bridge']} - command = _dnsmasq_cmd(network_ref) + 'NETWORK_ID': str(network_ref['id'])} + command = _dnsmasq_cmd(dev, network_ref) _execute(*command, addl_env=env) @utils.synchronized('radvd_start') -def update_ra(context, network_ref): - conffile = _ra_file(network_ref['bridge'], 'conf') +def update_ra(context, dev, network_ref): + conffile = _ra_file(dev, 'conf') with open(conffile, 'w') as f: conf_str = """ interface %s @@ -632,13 +644,13 @@ interface %s AdvAutonomous on; }; }; -""" % (network_ref['bridge'], network_ref['cidr_v6']) +""" % (dev, network_ref['cidr_v6']) f.write(conf_str) # Make sure radvd can actually read it (it setuid()s to "nobody") os.chmod(conffile, 0644) - pid = _ra_pid_for(network_ref['bridge']) + pid = _ra_pid_for(dev) # if radvd is already running, then tell it to reload if pid: @@ -651,7 +663,7 @@ interface %s LOG.debug(_('killing radvd threw %s'), exc) else: LOG.debug(_('Pid %d is stale, relaunching radvd'), pid) - command = _ra_cmd(network_ref) + command = _ra_cmd(dev) _execute(*command) @@ -696,20 +708,20 @@ def _device_exists(device): return not err -def _dnsmasq_cmd(net): +def _dnsmasq_cmd(dev, net): """Builds dnsmasq command.""" cmd = ['sudo', '-E', 'dnsmasq', '--strict-order', '--bind-interfaces', - '--interface=%s' % net['bridge'], + '--interface=%s' % dev, '--conf-file=%s' % FLAGS.dnsmasq_config_file, '--domain=%s' % FLAGS.dhcp_domain, - '--pid-file=%s' % _dhcp_file(net['bridge'], 'pid'), + '--pid-file=%s' % _dhcp_file(dev, 'pid'), '--listen-address=%s' % net['dhcp_server'], '--except-interface=lo', '--dhcp-range=%s,static,120s' % net['dhcp_start'], '--dhcp-lease-max=%s' % len(netaddr.IPNetwork(net['cidr'])), - '--dhcp-hostsfile=%s' % _dhcp_file(net['bridge'], 'conf'), + '--dhcp-hostsfile=%s' % _dhcp_file(dev, 'conf'), '--dhcp-script=%s' % FLAGS.dhcpbridge, '--leasefile-ro'] if FLAGS.dns_server: @@ -717,18 +729,18 @@ def _dnsmasq_cmd(net): return cmd -def _ra_cmd(net): +def _ra_cmd(dev): """Builds radvd command.""" cmd = ['sudo', '-E', 'radvd', # '-u', 'nobody', - '-C', '%s' % _ra_file(net['bridge'], 'conf'), - '-p', '%s' % _ra_file(net['bridge'], 'pid')] + '-C', '%s' % _ra_file(dev, 'conf'), + '-p', '%s' % _ra_file(dev, 'pid')] return cmd -def _stop_dnsmasq(network): +def _stop_dnsmasq(dev): """Stops the dnsmasq instance for a given network.""" - pid = _dnsmasq_pid_for(network) + pid = _dnsmasq_pid_for(dev) if pid: try: @@ -737,49 +749,49 @@ def _stop_dnsmasq(network): LOG.debug(_('Killing dnsmasq threw %s'), exc) -def _dhcp_file(bridge, kind): - """Return path to a pid, leases or conf file for a bridge.""" +def _dhcp_file(dev, kind): + """Return path to a pid, leases or conf file for a bridge/device.""" if not os.path.exists(FLAGS.networks_path): os.makedirs(FLAGS.networks_path) return os.path.abspath('%s/nova-%s.%s' % (FLAGS.networks_path, - bridge, + dev, kind)) -def _ra_file(bridge, kind): - """Return path to a pid or conf file for a bridge.""" +def _ra_file(dev, kind): + """Return path to a pid or conf file for a bridge/device.""" if not os.path.exists(FLAGS.networks_path): os.makedirs(FLAGS.networks_path) return os.path.abspath('%s/nova-ra-%s.%s' % (FLAGS.networks_path, - bridge, + dev, kind)) -def _dnsmasq_pid_for(bridge): - """Returns the pid for prior dnsmasq instance for a bridge. +def _dnsmasq_pid_for(dev): + """Returns the pid for prior dnsmasq instance for a bridge/device. Returns None if no pid file exists. If machine has rebooted pid might be incorrect (caller should check). """ - pid_file = _dhcp_file(bridge, 'pid') + pid_file = _dhcp_file(dev, 'pid') if os.path.exists(pid_file): with open(pid_file, 'r') as f: return int(f.read()) -def _ra_pid_for(bridge): - """Returns the pid for prior radvd instance for a bridge. +def _ra_pid_for(dev): + """Returns the pid for prior radvd instance for a bridge/device. Returns None if no pid file exists. If machine has rebooted pid might be incorrect (caller should check). """ - pid_file = _ra_file(bridge, 'pid') + pid_file = _ra_file(dev, 'pid') if os.path.exists(pid_file): with open(pid_file, 'r') as f: @@ -795,3 +807,72 @@ def _ip_bridge_cmd(action, params, device): iptables_manager = IptablesManager() + +# Similar to compute virt layers, the Linux network node +# code uses a flexible driver model to support different ways +# of creating ethernet interfaces and attaching them to the network. +# In the case of a network host, these interfaces +# act as gateway/dhcp/vpn/etc. endpoints not VM interfaces. + + +def plug(network): + return interface_driver.plug(network) + + +def unplug(network): + return interface_driver.unplug(network) + + +class LinuxNetInterfaceDriver(object): + """Abstract class that defines generic network host API""" + """ for for all Linux interface drivers.""" + + def plug(self, network): + """Create Linux device, return device name""" + raise NotImplementedError() + + def unplug(self, network): + """Destory Linux device, return device name""" + raise NotImplementedError() + + +# plugs interfaces using Linux Bridge +class LinuxBridgeInterfaceDriver(LinuxNetInterfaceDriver): + + def plug(self, network): + self.driver.ensure_vlan_bridge(network['vlan'], + network['bridge'], + network['bridge_interface'], + network) + return network['bridge'] + + def unplug(self, network): + return network['bridge'] + + +# plugs interfaces using Open vSwitch +class LinuxOVSInterfaceDriver(LinuxNetInterfaceDriver): + + def plug(self, network): + dev = "gw-" + str(network['id']) + if not _device_exists(dev): + bridge = FLAGS.linuxnet_ovs_integration_bridge + mac_addr = utils.generate_mac_address() + _execute('sudo', 'ovs-vsctl', + '--', '--may-exist', 'add-port', bridge, dev, + '--', 'set', 'Interface', dev, "type=internal", + '--', 'set', 'Interface', dev, + "external-ids:iface-id=nova-%s" % dev, + '--', 'set', 'Interface', dev, + "external-ids:iface-status=active", + '--', 'set', 'Interface', dev, + "external-ids:attached-mac=%s" % mac_addr) + _execute('sudo', 'ip', 'link', 'set', + dev, "address", mac_addr) + return dev + + def unplug(self, network): + dev = "gw-" + str(network['id']) + return dev + +interface_driver = utils.import_object(FLAGS.linuxnet_interface_driver) diff --git a/nova/network/manager.py b/nova/network/manager.py index 4706da6ea..0fc55f441 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -504,7 +504,7 @@ class NetworkManager(manager.SchedulerDependentManager): def _allocate_mac_addresses(self, context, instance_id, networks): """Generates mac addresses and creates vif rows in db for them.""" for network in networks: - vif = {'address': self.generate_mac_address(), + vif = {'address': utils.generate_mac_address(), 'instance_id': instance_id, 'network_id': network['id']} # try FLAG times to create a vif record with a unique mac_address @@ -513,20 +513,12 @@ class NetworkManager(manager.SchedulerDependentManager): self.db.virtual_interface_create(context, vif) break except exception.VirtualInterfaceCreateException: - vif['address'] = self.generate_mac_address() + vif['address'] = utils.generate_mac_address() else: self.db.virtual_interface_delete_by_instance(context, instance_id) raise exception.VirtualInterfaceMacAddressException() - def generate_mac_address(self): - """Generate a mac address for a vif on an instance.""" - mac = [0x02, 0x16, 0x3e, - random.randint(0x00, 0x7f), - random.randint(0x00, 0xff), - random.randint(0x00, 0xff)] - return ':'.join(map(lambda x: "%02x" % x, mac)) - def add_fixed_ip_to_instance(self, context, instance_id, host, network_id): """Adds a fixed ip to an instance from specified network.""" networks = [self.db.network_get(context, network_id)] @@ -887,23 +879,21 @@ class VlanManager(RPCAllocateFixedIP, FloatingIP, NetworkManager): else: address = network_ref['vpn_public_address'] network_ref['dhcp_server'] = self._get_dhcp_ip(context, network_ref) - self.driver.ensure_vlan_bridge(network_ref['vlan'], - network_ref['bridge'], - network_ref['bridge_interface'], - network_ref) + dev = self.driver.plug(network_ref) + self.driver.initialize_gateway_device(dev, network_ref) # NOTE(vish): only ensure this forward if the address hasn't been set # manually. if address == FLAGS.vpn_ip and hasattr(self.driver, - "ensure_vlan_forward"): - self.driver.ensure_vlan_forward(FLAGS.vpn_ip, + "ensure_vpn_forward"): + self.driver.ensure_vpn_forward(FLAGS.vpn_ip, network_ref['vpn_public_port'], network_ref['vpn_private_address']) if not FLAGS.fake_network: - self.driver.update_dhcp(context, network_ref) + self.driver.update_dhcp(context, dev, network_ref) if(FLAGS.use_ipv6): - self.driver.update_ra(context, network_ref) - gateway = utils.get_my_linklocal(network_ref['bridge']) + self.driver.update_ra(context, dev, network_ref) + gateway = utils.get_my_linklocal(dev) self.db.network_update(context, network_ref['id'], {'gateway_v6': gateway}) diff --git a/nova/utils.py b/nova/utils.py index 8784a227d..d56fa614c 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -320,6 +320,15 @@ def get_my_linklocal(interface): " :%(ex)s") % locals()) +def generate_mac_address(): + """Generate an Ethernet MAC address.""" + mac = [0x02, 0x16, 0x3e, + random.randint(0x00, 0x7f), + random.randint(0x00, 0xff), + random.randint(0x00, 0xff)] + return ':'.join(map(lambda x: "%02x" % x, mac)) + + def utcnow(): """Overridable version of utils.utcnow.""" if utcnow.override_time: diff --git a/nova/virt/libvirt/vif.py b/nova/virt/libvirt/vif.py index aa566cc72..432a585ad 100644 --- a/nova/virt/libvirt/vif.py +++ b/nova/virt/libvirt/vif.py @@ -99,8 +99,8 @@ class LibvirtOpenVswitchDriver(VIFDriver): def plug(self, instance, network, mapping): vif_id = str(instance['id']) + "-" + str(network['id']) dev = "tap-%s" % vif_id + iface_id = "nova-" + vif_id if not linux_net._device_exists(dev): - iface_id = "nova-" + vif_id utils.execute('sudo', 'ip', 'tuntap', 'add', dev, 'mode', 'tap') utils.execute('sudo', 'ip', 'link', 'set', dev, 'up') utils.execute('sudo', 'ovs-vsctl', '--', '--may-exist', 'add-port', -- cgit From 857f4453efaca98ced3e07d55ee6f0188713e60e Mon Sep 17 00:00:00 2001 From: Dan Wendlandt Date: Mon, 1 Aug 2011 18:30:59 -0700 Subject: fix LinuxBridgeInterfaceDriver --- nova/network/linux_net.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index ee0ef0b85..ce1f53ab9 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -840,10 +840,10 @@ class LinuxNetInterfaceDriver(object): class LinuxBridgeInterfaceDriver(LinuxNetInterfaceDriver): def plug(self, network): - self.driver.ensure_vlan_bridge(network['vlan'], - network['bridge'], - network['bridge_interface'], - network) + ensure_vlan_bridge(network['vlan'], + network['bridge'], + network['bridge_interface'], + network) return network['bridge'] def unplug(self, network): -- cgit From dfc9c9c2b5e92e599bdeae4c03d3761215a0deca Mon Sep 17 00:00:00 2001 From: Dan Wendlandt Date: Thu, 4 Aug 2011 12:36:11 -0700 Subject: modify _setup_network for flatDHCP as well --- nova/network/linux_net.py | 12 ++++++++---- nova/network/manager.py | 13 +++++++------ 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index ce1f53ab9..7012cceeb 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -805,9 +805,6 @@ def _ip_bridge_cmd(action, params, device): cmd.extend(['dev', device]) return cmd - -iptables_manager = IptablesManager() - # Similar to compute virt layers, the Linux network node # code uses a flexible driver model to support different ways # of creating ethernet interfaces and attaching them to the network. @@ -840,10 +837,16 @@ class LinuxNetInterfaceDriver(object): class LinuxBridgeInterfaceDriver(LinuxNetInterfaceDriver): def plug(self, network): - ensure_vlan_bridge(network['vlan'], + if network.get('vlan', None) is not None: + ensure_vlan_bridge(network['vlan'], network['bridge'], network['bridge_interface'], network) + else: + ensure_bridge(network['bridge'], + network['bridge_interface'], + network) + return network['bridge'] def unplug(self, network): @@ -875,4 +878,5 @@ class LinuxOVSInterfaceDriver(LinuxNetInterfaceDriver): dev = "gw-" + str(network['id']) return dev +iptables_manager = IptablesManager() interface_driver = utils.import_object(FLAGS.linuxnet_interface_driver) diff --git a/nova/network/manager.py b/nova/network/manager.py index bedbe2c21..e7c84d478 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -785,14 +785,15 @@ class FlatDHCPManager(FloatingIP, RPCAllocateFixedIP, NetworkManager): def _setup_network(self, context, network_ref): """Sets up network on this host.""" network_ref['dhcp_server'] = self._get_dhcp_ip(context, network_ref) - self.driver.ensure_bridge(network_ref['bridge'], - network_ref['bridge_interface'], - network_ref) + + dev = self.driver.plug(network_ref) + self.driver.initialize_gateway_device(dev, network_ref) + if not FLAGS.fake_network: - self.driver.update_dhcp(context, network_ref) + self.driver.update_dhcp(context, dev, network_ref) if(FLAGS.use_ipv6): - self.driver.update_ra(context, network_ref) - gateway = utils.get_my_linklocal(network_ref['bridge']) + self.driver.update_ra(context, dev, network_ref) + gateway = utils.get_my_linklocal(dev) self.db.network_update(context, network_ref['id'], {'gateway_v6': gateway}) -- cgit From 18f09f165b5dca5f11253b143045b2ff7327532d Mon Sep 17 00:00:00 2001 From: Dan Wendlandt Date: Thu, 4 Aug 2011 16:20:38 -0700 Subject: move ensure_vlan_bridge,ensure_bridge,ensure_vlan to the bridge/vlan specific vif-plugging driver --- nova/network/linux_net.py | 166 ++++++++++++++++++++++++---------------------- nova/virt/libvirt/vif.py | 6 +- 2 files changed, 90 insertions(+), 82 deletions(-) diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index 7012cceeb..17b63a849 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -456,84 +456,6 @@ def floating_forward_rules(floating_ip, fixed_ip): '-s %s -j SNAT --to %s' % (fixed_ip, floating_ip))] -def ensure_vlan_bridge(vlan_num, bridge, bridge_interface, net_attrs=None): - """Create a vlan and bridge unless they already exist.""" - interface = ensure_vlan(vlan_num, bridge_interface) - ensure_bridge(bridge, interface, net_attrs) - return interface - - -@utils.synchronized('ensure_vlan', external=True) -def ensure_vlan(vlan_num, bridge_interface): - """Create a vlan unless it already exists.""" - interface = 'vlan%s' % vlan_num - if not _device_exists(interface): - LOG.debug(_('Starting VLAN inteface %s'), interface) - _execute('sudo', 'vconfig', 'set_name_type', 'VLAN_PLUS_VID_NO_PAD') - _execute('sudo', 'vconfig', 'add', bridge_interface, vlan_num) - _execute('sudo', 'ip', 'link', 'set', interface, 'up') - return interface - - -@utils.synchronized('ensure_bridge', external=True) -def ensure_bridge(bridge, interface, net_attrs=None): - """Create a bridge unless it already exists. - - :param interface: the interface to create the bridge on. - :param net_attrs: dictionary with attributes used to create the bridge. - - If net_attrs is set, it will add the net_attrs['gateway'] to the bridge - using net_attrs['broadcast'] and net_attrs['cidr']. It will also add - the ip_v6 address specified in net_attrs['cidr_v6'] if use_ipv6 is set. - - The code will attempt to move any ips that already exist on the interface - onto the bridge and reset the default gateway if necessary. - - """ - if not _device_exists(bridge): - LOG.debug(_('Starting Bridge interface for %s'), interface) - _execute('sudo', 'brctl', 'addbr', bridge) - _execute('sudo', 'brctl', 'setfd', bridge, 0) - # _execute('sudo brctl setageing %s 10' % bridge) - _execute('sudo', 'brctl', 'stp', bridge, 'off') - - if interface: - out, err = _execute('sudo', 'brctl', 'addif', bridge, interface, - check_exit_code=False) - - # NOTE(vish): This will break if there is already an ip on the - # interface, so we move any ips to the bridge - gateway = None - out, err = _execute('sudo', 'route', '-n') - for line in out.split('\n'): - fields = line.split() - if fields and fields[0] == '0.0.0.0' and fields[-1] == interface: - gateway = fields[1] - _execute('sudo', 'route', 'del', 'default', 'gw', gateway, - 'dev', interface, check_exit_code=False) - out, err = _execute('sudo', 'ip', 'addr', 'show', 'dev', interface, - 'scope', 'global') - for line in out.split('\n'): - fields = line.split() - if fields and fields[0] == 'inet': - params = fields[1:-1] - _execute(*_ip_bridge_cmd('del', params, fields[-1])) - _execute(*_ip_bridge_cmd('add', params, bridge)) - if gateway: - _execute('sudo', 'route', 'add', 'default', 'gw', gateway) - - if (err and err != "device %s is already a member of a bridge; can't " - "enslave it to bridge %s.\n" % (interface, bridge)): - raise exception.Error('Failed to add interface: %s' % err) - - iptables_manager.ipv4['filter'].add_rule('FORWARD', - '--in-interface %s -j ACCEPT' % \ - bridge) - iptables_manager.ipv4['filter'].add_rule('FORWARD', - '--out-interface %s -j ACCEPT' % \ - bridge) - - def initialize_gateway_device(dev, network_ref): if not network_ref: return @@ -838,12 +760,14 @@ class LinuxBridgeInterfaceDriver(LinuxNetInterfaceDriver): def plug(self, network): if network.get('vlan', None) is not None: - ensure_vlan_bridge(network['vlan'], + LinuxBridgeInterfaceDriver.ensure_vlan_bridge( + network['vlan'], network['bridge'], network['bridge_interface'], network) else: - ensure_bridge(network['bridge'], + LinuxBridgeInterfaceDriver.ensure_bridge( + network['bridge'], network['bridge_interface'], network) @@ -852,6 +776,88 @@ class LinuxBridgeInterfaceDriver(LinuxNetInterfaceDriver): def unplug(self, network): return network['bridge'] + @classmethod + def ensure_vlan_bridge(_self, vlan_num, bridge, bridge_interface, + net_attrs=None): + """Create a vlan and bridge unless they already exist.""" + interface = LinuxBridgeInterfaceDriver.ensure_vlan(vlan_num, + bridge_interface) + LinuxBridgeInterfaceDriver.ensure_bridge(bridge, interface, net_attrs) + return interface + + @classmethod + @utils.synchronized('ensure_vlan', external=True) + def ensure_vlan(_self, vlan_num, bridge_interface): + """Create a vlan unless it already exists.""" + interface = 'vlan%s' % vlan_num + if not _device_exists(interface): + LOG.debug(_('Starting VLAN inteface %s'), interface) + _execute('sudo', 'vconfig', 'set_name_type', + 'VLAN_PLUS_VID_NO_PAD') + _execute('sudo', 'vconfig', 'add', bridge_interface, vlan_num) + _execute('sudo', 'ip', 'link', 'set', interface, 'up') + return interface + + @classmethod + @utils.synchronized('ensure_bridge', external=True) + def ensure_bridge(_self, bridge, interface, net_attrs=None): + """Create a bridge unless it already exists. + + :param interface: the interface to create the bridge on. + :param net_attrs: dictionary with attributes used to create bridge. + + If net_attrs is set, it will add the net_attrs['gateway'] to the bridge + using net_attrs['broadcast'] and net_attrs['cidr']. It will also add + the ip_v6 address specified in net_attrs['cidr_v6'] if use_ipv6 is set. + + The code will attempt to move any ips that already exist on the + interface onto the bridge and reset the default gateway if necessary. + + """ + if not _device_exists(bridge): + LOG.debug(_('Starting Bridge interface for %s'), interface) + _execute('sudo', 'brctl', 'addbr', bridge) + _execute('sudo', 'brctl', 'setfd', bridge, 0) + # _execute('sudo brctl setageing %s 10' % bridge) + _execute('sudo', 'brctl', 'stp', bridge, 'off') + + if interface: + out, err = _execute('sudo', 'brctl', 'addif', bridge, interface, + check_exit_code=False) + + # NOTE(vish): This will break if there is already an ip on the + # interface, so we move any ips to the bridge + gateway = None + out, err = _execute('sudo', 'route', '-n') + for line in out.split('\n'): + fields = line.split() + if fields and fields[0] == '0.0.0.0' and \ + fields[-1] == interface: + gateway = fields[1] + _execute('sudo', 'route', 'del', 'default', 'gw', gateway, + 'dev', interface, check_exit_code=False) + out, err = _execute('sudo', 'ip', 'addr', 'show', 'dev', interface, + 'scope', 'global') + for line in out.split('\n'): + fields = line.split() + if fields and fields[0] == 'inet': + params = fields[1:-1] + _execute(*_ip_bridge_cmd('del', params, fields[-1])) + _execute(*_ip_bridge_cmd('add', params, bridge)) + if gateway: + _execute('sudo', 'route', 'add', 'default', 'gw', gateway) + + if (err and err != "device %s is already a member of a bridge;" + "can't enslave it to bridge %s.\n" % (interface, bridge)): + raise exception.Error('Failed to add interface: %s' % err) + + iptables_manager.ipv4['filter'].add_rule('FORWARD', + '--in-interface %s -j ACCEPT' % \ + bridge) + iptables_manager.ipv4['filter'].add_rule('FORWARD', + '--out-interface %s -j ACCEPT' % \ + bridge) + # plugs interfaces using Open vSwitch class LinuxOVSInterfaceDriver(LinuxNetInterfaceDriver): diff --git a/nova/virt/libvirt/vif.py b/nova/virt/libvirt/vif.py index eef582fac..f42eeec98 100644 --- a/nova/virt/libvirt/vif.py +++ b/nova/virt/libvirt/vif.py @@ -79,12 +79,14 @@ class LibvirtBridgeDriver(VIFDriver): LOG.debug(_('Ensuring vlan %(vlan)s and bridge %(bridge)s'), {'vlan': network['vlan'], 'bridge': network['bridge']}) - linux_net.ensure_vlan_bridge(network['vlan'], + linux_net.LinuxBridgeInterfaceDriver.ensure_vlan_bridge( + network['vlan'], network['bridge'], network['bridge_interface']) else: LOG.debug(_("Ensuring bridge %s"), network['bridge']) - linux_net.ensure_bridge(network['bridge'], + linux_net.LinuxBridgeInterfaceDriver.ensure_bridge( + network['bridge'], network['bridge_interface']) return self._get_configurations(network, mapping) -- cgit From c81febc28a602989636e77d1b3e9a75741e04352 Mon Sep 17 00:00:00 2001 From: Dan Prince Date: Tue, 9 Aug 2011 16:00:54 -0400 Subject: Updated the EC2 metadata controller so that it returns the correct value for instance-type metadata. --- nova/api/ec2/cloud.py | 2 +- smoketests/test_netadmin.py | 19 +++++++++++++++---- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index f64a92d12..9538a31e2 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -299,7 +299,7 @@ class CloudController(object): 'hostname': hostname, 'instance-action': 'none', 'instance-id': ec2_id, - 'instance-type': instance_ref['instance_type'], + 'instance-type': instance_ref['instance_type'].name, 'local-hostname': hostname, 'local-ipv4': address, 'placement': {'availability-zone': availability_zone}, diff --git a/smoketests/test_netadmin.py b/smoketests/test_netadmin.py index 8c8fa35b8..ef73e6f4c 100644 --- a/smoketests/test_netadmin.py +++ b/smoketests/test_netadmin.py @@ -107,14 +107,18 @@ class AddressTests(base.UserSmokeTestCase): class SecurityGroupTests(base.UserSmokeTestCase): - def __public_instance_is_accessible(self): - id_url = "latest/meta-data/instance-id" + def __get_metadata_item(self, category): + id_url = "latest/meta-data/%s" % category options = "-f -s --max-time 1" command = "curl %s %s/%s" % (options, self.data['public_ip'], id_url) status, output = commands.getstatusoutput(command) - instance_id = output.strip() + value = output.strip() if status > 0: return False + return value + + def __public_instance_is_accessible(self): + instance_id = self.__get_metadata_item('instance-id') if not instance_id: return False if instance_id != self.data['instance'].id: @@ -166,7 +170,14 @@ class SecurityGroupTests(base.UserSmokeTestCase): finally: result = self.conn.disassociate_address(self.data['public_ip']) - def test_005_can_revoke_security_group_ingress(self): + def test_005_validate_metadata(self): + + instance = self.data['instance'] + self.assertTrue(instance.instance_type, + self.__get_metadata_item("instance-type")) + #FIXME(dprince): validate more metadata here + + def test_006_can_revoke_security_group_ingress(self): self.assertTrue(self.conn.revoke_security_group(TEST_GROUP, ip_protocol='tcp', from_port=80, -- cgit From f9cf0d334330f034d0e0fb2ae8c88dda38e62832 Mon Sep 17 00:00:00 2001 From: "vladimir.p" Date: Thu, 11 Aug 2011 11:54:35 -0700 Subject: Added search instance by metadata. get_all_by_filters should filter deleted --- Authors | 1 + nova/db/sqlalchemy/api.py | 20 ++++++++++++++- nova/tests/test_compute.py | 63 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 83 insertions(+), 1 deletion(-) diff --git a/Authors b/Authors index e639cbf76..ccd70baaf 100644 --- a/Authors +++ b/Authors @@ -103,6 +103,7 @@ Tushar Patil Vasiliy Shlykov Vishvananda Ishaya Vivek Y S +Vladimir Popovski William Wolf Yoshiaki Tamura Youcef Laribi diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 8119cdfb8..65977a8bf 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -1175,6 +1175,19 @@ def instance_get_all_by_filters(context, filters): return True return False + def _regexp_filter_by_metadata(instance, meta): + inst_metadata = [{node['key']: node['value']} \ + for node in instance['metadata']] + if isinstance(meta, list): + for node in meta: + if node not in inst_metadata: + return False + elif isinstance(meta, dict): + for k, v in meta.iteritems(): + if {k: v} not in inst_metadata: + return False + return True + def _regexp_filter_by_column(instance, filter_name, filter_re): try: v = getattr(instance, filter_name) @@ -1232,7 +1245,9 @@ def instance_get_all_by_filters(context, filters): query_prefix = _exact_match_filter(query_prefix, filter_name, filters.pop(filter_name)) - instances = query_prefix.all() + instances = query_prefix.\ + filter_by(deleted=can_read_deleted(context)).\ + all() if not instances: return [] @@ -1248,6 +1263,9 @@ def instance_get_all_by_filters(context, filters): filter_re = re.compile(str(filters[filter_name])) if filter_func: filter_l = lambda instance: filter_func(instance, filter_re) + elif filter_name == 'metadata': + filter_l = lambda instance: _regexp_filter_by_metadata(instance, + filters[filter_name]) else: filter_l = lambda instance: _regexp_filter_by_column(instance, filter_name, filter_re) diff --git a/nova/tests/test_compute.py b/nova/tests/test_compute.py index 73c9bd78d..18daa970e 100644 --- a/nova/tests/test_compute.py +++ b/nova/tests/test_compute.py @@ -1323,6 +1323,69 @@ class ComputeTestCase(test.TestCase): db.instance_destroy(c, instance_id2) db.instance_destroy(c, instance_id3) + def test_get_all_by_metadata(self): + """Test searching instances by metadata""" + + c = context.get_admin_context() + instance_id0 = self._create_instance() + instance_id1 = self._create_instance({ + 'metadata': {'key1': 'value1'}}) + instance_id2 = self._create_instance({ + 'metadata': {'key2': 'value2'}}) + instance_id3 = self._create_instance({ + 'metadata': {'key3': 'value3'}}) + instance_id4 = self._create_instance({ + 'metadata': {'key3': 'value3', + 'key4': 'value4'}}) + + # get all instances + instances = self.compute_api.get_all(c, + search_opts={'metadata': {}}) + self.assertEqual(len(instances), 5) + + # wrong key/value combination + instances = self.compute_api.get_all(c, + search_opts={'metadata': {'key1': 'value3'}}) + self.assertEqual(len(instances), 0) + + # non-existing keys + instances = self.compute_api.get_all(c, + search_opts={'metadata': {'key5': 'value1'}}) + self.assertEqual(len(instances), 0) + + # find existing instance + instances = self.compute_api.get_all(c, + search_opts={'metadata': {'key2': 'value2'}}) + self.assertEqual(len(instances), 1) + self.assertEqual(instances[0].id, instance_id2) + + instances = self.compute_api.get_all(c, + search_opts={'metadata': {'key3': 'value3'}}) + self.assertEqual(len(instances), 2) + instance_ids = [instance.id for instance in instances] + self.assertTrue(instance_id3 in instance_ids) + self.assertTrue(instance_id4 in instance_ids) + + # multiple criterias as a dict + instances = self.compute_api.get_all(c, + search_opts={'metadata': {'key3': 'value3', + 'key4': 'value4'}}) + self.assertEqual(len(instances), 1) + self.assertEqual(instances[0].id, instance_id4) + + # multiple criterias as a list + instances = self.compute_api.get_all(c, + search_opts={'metadata': [{'key4': 'value4'}, + {'key3': 'value3'}]}) + self.assertEqual(len(instances), 1) + self.assertEqual(instances[0].id, instance_id4) + + db.instance_destroy(c, instance_id0) + db.instance_destroy(c, instance_id1) + db.instance_destroy(c, instance_id2) + db.instance_destroy(c, instance_id3) + db.instance_destroy(c, instance_id4) + @staticmethod def _parse_db_block_device_mapping(bdm_ref): attr_list = ('delete_on_termination', 'device_name', 'no_device', -- cgit From b29bc97d5a69abe71dea5b9ff9dcfc65fcd59cc9 Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Thu, 11 Aug 2011 17:10:25 -0500 Subject: Check compressed image size and PEP8 cleanup. --- nova/compute/manager.py | 40 +++++++++++++++++++++++ nova/exception.py | 4 +++ nova/tests/api/openstack/contrib/test_keypairs.py | 17 ++++++++-- 3 files changed, 59 insertions(+), 2 deletions(-) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index d38213083..1c3485342 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -321,10 +321,50 @@ class ComputeManager(manager.SchedulerDependentManager): def _run_instance(self, context, instance_id, **kwargs): """Launch a new instance with specified options.""" + def _check_image_size(): + """Ensure image is smaller than the maximum size allowed by the + instance_type. + + The image stored in Glance is potentially compressed, so we use two + checks to ensure that the size isn't exceeded: + + 1) This one - checks compressed size, this a quick check to + eliminate any images which are obviously too large + + 2) Check uncompressed size in nova.virt.xenapi.vm_utils. This + is a slower check since it requires uncompressing the entire + image, but is accurate because it reflects the image's + actual size. + """ + image_href = instance['image_ref'] + image_service, image_id = nova.image.get_image_service(image_href) + image_meta = image_service.show(context, image_id) + size_bytes = image_meta['size'] + + instance_type_id = instance['instance_type_id'] + instance_type = self.db.instance_type_get(context, + instance_type_id) + allowed_size_gb = instance_type['local_gb'] + allowed_size_bytes = allowed_size_gb * 1024 * 1024 * 1024 + + LOG.debug(_("image_id=%(image_id)d, image_size_bytes=" + "%(size_bytes)d, allowed_size_bytes=" + "%(allowed_size_bytes)d") % locals()) + + if size_bytes > allowed_size_bytes: + LOG.info(_("Image '%(image_id)d' size %(size_bytes)d exceeded" + " instance_type allowed size " + "%(allowed_size_bytes)d") + % locals()) + raise exception.ImageTooLarge() + context = context.elevated() instance = self.db.instance_get(context, instance_id) if instance['name'] in self.driver.list_instances(): raise exception.Error(_("Instance has already been created")) + + _check_image_size() + LOG.audit(_("instance %s: starting..."), instance_id, context=context) updates = {} diff --git a/nova/exception.py b/nova/exception.py index 0d60cb0bf..77ebf3c88 100644 --- a/nova/exception.py +++ b/nova/exception.py @@ -717,3 +717,7 @@ class CannotResizeToSameSize(NovaException): class CannotResizeToSmallerSize(NovaException): message = _("Resizing to a smaller size is not supported.") + + +class ImageTooLarge(NovaException): + message = _("Image is larger than instance type allows") diff --git a/nova/tests/api/openstack/contrib/test_keypairs.py b/nova/tests/api/openstack/contrib/test_keypairs.py index c9dc34d65..cbc815b1a 100644 --- a/nova/tests/api/openstack/contrib/test_keypairs.py +++ b/nova/tests/api/openstack/contrib/test_keypairs.py @@ -28,6 +28,7 @@ def fake_keypair(name): 'fingerprint': 'FAKE_FINGERPRINT', 'name': name} + def db_key_pair_get_all_by_user(self, user_id): return [fake_keypair('FAKE')] @@ -78,7 +79,20 @@ class KeypairsTest(test.TestCase): def test_keypair_import(self): body = {'keypair': {'name': 'create_test', - 'public_key': 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDBYIznAx9D7118Q1VKGpXy2HDiKyUTM8XcUuhQpo0srqb9rboUp4a9NmCwpWpeElDLuva707GOUnfaBAvHBwsRXyxHJjRaI6YQj2oLJwqvaSaWUbyT1vtryRqy6J3TecN0WINY71f4uymiMZP0wby4bKBcYnac8KiCIlvkEl0ETjkOGUq8OyWRmn7ljj5SESEUdBP0JnuTFKddWTU/wD6wydeJaUhBTqOlHn0kX1GyqoNTE1UEhcM5ZRWgfUZfTjVyDF2kGj3vJLCJtJ8LoGcj7YaN4uPg1rBle+izwE/tLonRrds+cev8p6krSSrxWOwBbHkXa6OciiJDvkRzJXzf'}} + 'public_key': 'ssh-rsa ' + 'AAAAB3NzaC1yc2EAAAADAQABAAABAQDBY' + 'IznAx9D7118Q1VKGpXy2HDiKyUTM8XcUu' + 'hQpo0srqb9rboUp4a9NmCwpWpeElDLuva' + '707GOUnfaBAvHBwsRXyxHJjRaI6YQj2oL' + 'JwqvaSaWUbyT1vtryRqy6J3TecN0WINY7' + '1f4uymiMZP0wby4bKBcYnac8KiCIlvkEl' + '0ETjkOGUq8OyWRmn7ljj5SESEUdBP0Jnu' + 'TFKddWTU/wD6wydeJaUhBTqOlHn0kX1Gy' + 'qoNTE1UEhcM5ZRWgfUZfTjVyDF2kGj3vJ' + 'LCJtJ8LoGcj7YaN4uPg1rBle+izwE/tLo' + 'nRrds+cev8p6krSSrxWOwBbHkXa6OciiJ' + 'DvkRzJXzf'}} + req = webob.Request.blank('/v1.1/os-keypairs') req.method = 'POST' req.body = json.dumps(body) @@ -96,4 +110,3 @@ class KeypairsTest(test.TestCase): req.headers['Content-Type'] = 'application/json' res = req.get_response(fakes.wsgi_app()) self.assertEqual(res.status_int, 202) - -- cgit From ee8ef9ab1de284ec77d33bb27741f010f9a63961 Mon Sep 17 00:00:00 2001 From: Jason Koelker Date: Fri, 12 Aug 2011 11:55:38 -0500 Subject: use subnet iteration from netaddr for subnet calculation --- nova/network/manager.py | 55 ++++++++++++++++++++++++++----------------------- 1 file changed, 29 insertions(+), 26 deletions(-) diff --git a/nova/network/manager.py b/nova/network/manager.py index 8fc6a295f..93f571c21 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -45,6 +45,7 @@ topologies. All of the network commands are issued to a subclass of """ import datetime +import itertools import math import netaddr import socket @@ -618,61 +619,63 @@ class NetworkManager(manager.SchedulerDependentManager): network_size, cidr_v6, gateway_v6, bridge, bridge_interface, dns1=None, dns2=None, **kwargs): """Create networks based on parameters.""" + # NOTE(jkoelker): these are dummy values to make sure iter works + fixed_net_v4 = netaddr.IPNetwork('0/32') + fixed_net_v6 = netaddr.IPNetwork('::0/128') + subnets_v4 = [] + subnets_v6 = [] + + subnet_bits = int(math.ceil(math.log(network_size, 2)) + if cidr_v6: fixed_net_v6 = netaddr.IPNetwork(cidr_v6) - significant_bits_v6 = 64 - network_size_v6 = 1 << 64 + prefixlen_v6 = 128 - subnet_bits + subnets_v6 = fixed_net_v6.subnet(prefixlen_v6, count=num_networks) if cidr: fixed_net = netaddr.IPNetwork(cidr) - significant_bits = 32 - int(math.log(network_size, 2)) + prefixlen_v4 = 32 - subnet_bits + subnets_v4 = fixed_net_v4.subnet(prefixlen_v4, count=num_networks) - for index in range(num_networks): + subnets = itertools.izip_longest(subnets_v4, subnets_v6) + for index, (subnet_v4, subnet_v6) in enumerate(subnets): net = {} net['bridge'] = bridge net['bridge_interface'] = bridge_interface + net['multi_host'] = multi_host + net['dns1'] = dns1 net['dns2'] = dns2 - if cidr: - start = index * network_size - project_net = netaddr.IPNetwork('%s/%s' % (fixed_net[start], - significant_bits)) - net['cidr'] = str(project_net) - net['multi_host'] = multi_host - net['netmask'] = str(project_net.netmask) - net['gateway'] = str(project_net[1]) - net['broadcast'] = str(project_net.broadcast) - net['dhcp_start'] = str(project_net[2]) - if num_networks > 1: net['label'] = '%s_%d' % (label, index) else: net['label'] = label - if cidr_v6: - start_v6 = index * network_size_v6 - cidr_v6 = '%s/%s' % (fixed_net_v6[start_v6], - significant_bits_v6) - net['cidr_v6'] = cidr_v6 - - project_net_v6 = netaddr.IPNetwork(cidr_v6) + if cidr: + net['cidr'] = str(subnet_v4) + net['netmask'] = str(subnet_v4.netmask) + net['gateway'] = str(subnet_v4[1]) + net['broadcast'] = str(subnet_v4.broadcast) + net['dhcp_start'] = str(subnet_v4[2]) + if cidr_v6: + net['cidr_v6'] = str(subnet_v6) if gateway_v6: # use a pre-defined gateway if one is provided net['gateway_v6'] = str(gateway_v6) else: - net['gateway_v6'] = str(project_net_v6[1]) + net['gateway_v6'] = str(subnet_v6[1]) - net['netmask_v6'] = str(project_net_v6._prefixlen) + net['netmask_v6'] = str(subnet_v6._prefixlen) if kwargs.get('vpn', False): # this bit here is for vlan-manager del net['dns1'] del net['dns2'] vlan = kwargs['vlan_start'] + index - net['vpn_private_address'] = str(project_net[2]) - net['dhcp_start'] = str(project_net[3]) + net['vpn_private_address'] = str(subnet_v4[2]) + net['dhcp_start'] = str(subnet_v4[3]) net['vlan'] = vlan net['bridge'] = 'br%s' % vlan -- cgit From 8f5f2c651846f8a3ff66821451216552d71c8fe6 Mon Sep 17 00:00:00 2001 From: Jason Koelker Date: Fri, 12 Aug 2011 12:00:23 -0500 Subject: forgot the closing paren --- nova/network/manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/network/manager.py b/nova/network/manager.py index 93f571c21..889e1f9cd 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -625,7 +625,7 @@ class NetworkManager(manager.SchedulerDependentManager): subnets_v4 = [] subnets_v6 = [] - subnet_bits = int(math.ceil(math.log(network_size, 2)) + subnet_bits = int(math.ceil(math.log(network_size, 2))) if cidr_v6: fixed_net_v6 = netaddr.IPNetwork(cidr_v6) -- cgit From 21707674ce862f4e12a8ee9db665829f09d29467 Mon Sep 17 00:00:00 2001 From: Jason Koelker Date: Fri, 12 Aug 2011 12:03:02 -0500 Subject: don't require ipv4 --- bin/nova-manage | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/nova-manage b/bin/nova-manage index 077a89d6f..1b7a2bf0a 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -719,7 +719,7 @@ class NetworkCommands(object): # sanitize other input using FLAGS if necessary if not num_networks: num_networks = FLAGS.num_networks - if not network_size: + if not network_size and fixed_range_v4: fixnet = netaddr.IPNetwork(fixed_range_v4) each_subnet_size = fixnet.size / int(num_networks) if each_subnet_size > FLAGS.network_size: -- cgit From 0beef1b24ee63f554f5478d54ee32f86fe5f4f2c Mon Sep 17 00:00:00 2001 From: Jason Koelker Date: Fri, 12 Aug 2011 12:06:57 -0500 Subject: make sure network_size gets set --- bin/nova-manage | 3 +++ 1 file changed, 3 insertions(+) diff --git a/bin/nova-manage b/bin/nova-manage index 1b7a2bf0a..25655125d 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -741,6 +741,9 @@ class NetworkCommands(object): if not dns1 and FLAGS.flat_network_dns: dns1 = FLAGS.flat_network_dns + if not network_size: + network_size = FLAGS.network_size + # create the network net_manager = utils.import_object(FLAGS.network_manager) net_manager.create_networks(context.get_admin_context(), -- cgit From 07aae460e848af51667537d56ec8b89d0c79f048 Mon Sep 17 00:00:00 2001 From: Jason Koelker Date: Fri, 12 Aug 2011 12:11:13 -0500 Subject: only run if the subnet and cidr exist --- nova/network/manager.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nova/network/manager.py b/nova/network/manager.py index 889e1f9cd..f26263f2e 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -652,14 +652,14 @@ class NetworkManager(manager.SchedulerDependentManager): else: net['label'] = label - if cidr: + if cidr and subnet_v4: net['cidr'] = str(subnet_v4) net['netmask'] = str(subnet_v4.netmask) net['gateway'] = str(subnet_v4[1]) net['broadcast'] = str(subnet_v4.broadcast) net['dhcp_start'] = str(subnet_v4[2]) - if cidr_v6: + if cidr_v6 and subnet_v6: net['cidr_v6'] = str(subnet_v6) if gateway_v6: # use a pre-defined gateway if one is provided -- cgit From 411ee0c1a1901de7c9b7ceae1d41b7742fce609e Mon Sep 17 00:00:00 2001 From: Jason Koelker Date: Fri, 12 Aug 2011 12:13:14 -0500 Subject: only run if the subnet and cidr exist --- nova/network/manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/network/manager.py b/nova/network/manager.py index f26263f2e..ccc899d99 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -689,7 +689,7 @@ class NetworkManager(manager.SchedulerDependentManager): if not network: raise ValueError(_('Network already exists!')) - if network and cidr: + if network and cidr and subnet_v4: self._create_fixed_ips(context, network['id']) @property -- cgit From 24796debe819641b1cba58ba966b0d6d5a253fd8 Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Fri, 12 Aug 2011 12:14:40 -0500 Subject: Fixed unit tests. --- nova/compute/manager.py | 15 ++++++++++++++- nova/tests/scheduler/test_scheduler.py | 4 +++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 1c3485342..f0184161e 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -336,10 +336,22 @@ class ComputeManager(manager.SchedulerDependentManager): image, but is accurate because it reflects the image's actual size. """ + # NOTE(jk0): image_ref is defined in the DB model, image_href is + # used by the image service. This should be refactored to be + # consistent. image_href = instance['image_ref'] image_service, image_id = nova.image.get_image_service(image_href) image_meta = image_service.show(context, image_id) - size_bytes = image_meta['size'] + + try: + size_bytes = image_meta['size'] + except KeyError: + # Size is not a required field in the image service (yet), so + # we are unable to rely on it being there even though it's in + # glance. + + # TODO(jk0): Should size be required in the image service? + return instance_type_id = instance['instance_type_id'] instance_type = self.db.instance_type_get(context, @@ -360,6 +372,7 @@ class ComputeManager(manager.SchedulerDependentManager): context = context.elevated() instance = self.db.instance_get(context, instance_id) + if instance['name'] in self.driver.list_instances(): raise exception.Error(_("Instance has already been created")) diff --git a/nova/tests/scheduler/test_scheduler.py b/nova/tests/scheduler/test_scheduler.py index 7a26fd1bb..ebaf89624 100644 --- a/nova/tests/scheduler/test_scheduler.py +++ b/nova/tests/scheduler/test_scheduler.py @@ -256,7 +256,9 @@ class SimpleDriverTestCase(test.TestCase): def _create_instance(self, **kwargs): """Create a test instance""" inst = {} - inst['image_id'] = 1 + # NOTE(jk0): If an integer is passed as the image_ref, the image + # service will use the default image service (in this case, the fake). + inst['image_ref'] = '1' inst['reservation_id'] = 'r-fakeres' inst['user_id'] = self.user_id inst['project_id'] = self.project_id -- cgit From dbaa1c2299d3b97273698050b372b9714324706a Mon Sep 17 00:00:00 2001 From: Jason Koelker Date: Fri, 12 Aug 2011 12:23:50 -0500 Subject: need to actually assign the v4 network --- nova/network/manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/network/manager.py b/nova/network/manager.py index ccc899d99..32e236073 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -633,7 +633,7 @@ class NetworkManager(manager.SchedulerDependentManager): subnets_v6 = fixed_net_v6.subnet(prefixlen_v6, count=num_networks) if cidr: - fixed_net = netaddr.IPNetwork(cidr) + fixed_net_v4 = netaddr.IPNetwork(cidr) prefixlen_v4 = 32 - subnet_bits subnets_v4 = fixed_net_v4.subnet(prefixlen_v4, count=num_networks) -- cgit From 8c9eedb4b4dd9653cca302ae4bbd23d895761aee Mon Sep 17 00:00:00 2001 From: "vladimir.p" Date: Fri, 12 Aug 2011 14:18:25 -0700 Subject: reworked test_extensions code to avoid constant merge conflicts with newly added ext --- nova/tests/api/openstack/test_extensions.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/nova/tests/api/openstack/test_extensions.py b/nova/tests/api/openstack/test_extensions.py index ea8fe68a7..e9f44af6a 100644 --- a/nova/tests/api/openstack/test_extensions.py +++ b/nova/tests/api/openstack/test_extensions.py @@ -84,6 +84,17 @@ class ExtensionControllerTest(test.TestCase): super(ExtensionControllerTest, self).setUp() ext_path = os.path.join(os.path.dirname(__file__), "extensions") self.flags(osapi_extensions_path=ext_path) + self.ext_list = [ + "FlavorExtraSpecs", + "Floating_ips", + "Fox In Socks", + "Hosts", + "Keypairs", + "Multinic", + "SecurityGroups", + "Volumes", + ] + self.ext_list.sort() def test_list_extensions_json(self): app = openstack.APIRouterV11() @@ -96,9 +107,7 @@ class ExtensionControllerTest(test.TestCase): data = json.loads(response.body) names = [x['name'] for x in data['extensions']] names.sort() - self.assertEqual(names, ["FlavorExtraSpecs", "Floating_ips", - "Fox In Socks", "Hosts", "Keypairs", "Multinic", "SecurityGroups", - "Volumes"]) + self.assertEqual(names, self.ext_list) # Make sure that at least Fox in Sox is correct. (fox_ext,) = [ @@ -145,7 +154,7 @@ class ExtensionControllerTest(test.TestCase): # Make sure we have all the extensions. exts = root.findall('{0}extension'.format(NS)) - self.assertEqual(len(exts), 8) + self.assertEqual(len(exts), len(self.ext_list)) # Make sure that at least Fox in Sox is correct. (fox_ext,) = [x for x in exts if x.get('alias') == 'FOXNSOX'] -- cgit From b60fa0d09d02066863736a3e98f07094c4db05a6 Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Fri, 12 Aug 2011 16:18:48 -0500 Subject: Check uncompressed VHD size. --- nova/tests/xenapi/stubs.py | 4 +-- nova/virt/xenapi/fake.py | 1 + nova/virt/xenapi/vm_utils.py | 74 +++++++++++++++++++++++++++++++++++++++----- nova/virt/xenapi/vmops.py | 6 ++-- 4 files changed, 72 insertions(+), 13 deletions(-) diff --git a/nova/tests/xenapi/stubs.py b/nova/tests/xenapi/stubs.py index 0d0f84e32..a6a1febd6 100644 --- a/nova/tests/xenapi/stubs.py +++ b/nova/tests/xenapi/stubs.py @@ -28,10 +28,10 @@ from nova import utils def stubout_instance_snapshot(stubs): @classmethod - def fake_fetch_image(cls, context, session, instance_id, image, user, + def fake_fetch_image(cls, context, session, instance, image, user, project, type): from nova.virt.xenapi.fake import create_vdi - name_label = "instance-%s" % instance_id + name_label = "instance-%s" % instance.id #TODO: create fake SR record sr_ref = "fakesr" vdi_ref = create_vdi(name_label=name_label, read_only=False, diff --git a/nova/virt/xenapi/fake.py b/nova/virt/xenapi/fake.py index d5ac39473..eecd60d58 100644 --- a/nova/virt/xenapi/fake.py +++ b/nova/virt/xenapi/fake.py @@ -140,6 +140,7 @@ def create_vdi(name_label, read_only, sr_ref, sharable): 'location': '', 'xenstore_data': '', 'sm_config': {}, + 'physical_utilisation': '123', 'VBDs': {}}) diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index 6c44d53d4..e86f72347 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -31,6 +31,7 @@ import uuid from xml.dom import minidom import glance.client +from nova import db from nova import exception from nova import flags import nova.image @@ -368,7 +369,7 @@ class VMHelper(HelperBase): session.wait_for_task(task, instance.id) @classmethod - def fetch_image(cls, context, session, instance_id, image, user_id, + def fetch_image(cls, context, session, instance, image, user_id, project_id, image_type): """Fetch image from glance based on image type. @@ -377,18 +378,19 @@ class VMHelper(HelperBase): """ if image_type == ImageType.DISK_VHD: return cls._fetch_image_glance_vhd(context, - session, instance_id, image, image_type) + session, instance, image, image_type) else: return cls._fetch_image_glance_disk(context, - session, instance_id, image, image_type) + session, instance, image, image_type) @classmethod - def _fetch_image_glance_vhd(cls, context, session, instance_id, image, + def _fetch_image_glance_vhd(cls, context, session, instance, image, image_type): """Tell glance to download an image and put the VHDs into the SR Returns: A list of dictionaries that describe VDIs """ + instance_id = instance.id LOG.debug(_("Asking xapi to fetch vhd image %(image)s") % locals()) sr_ref = safe_find_sr(session) @@ -422,17 +424,57 @@ class VMHelper(HelperBase): cls.scan_sr(session, instance_id, sr_ref) - # Pull out the UUID of the first VDI - vdi_uuid = vdis[0]['vdi_uuid'] + # Pull out the UUID of the first VDI (which is the os VDI) + os_vdi_uuid = vdis[0]['vdi_uuid'] + # Set the name-label to ease debugging - vdi_ref = session.get_xenapi().VDI.get_by_uuid(vdi_uuid) + vdi_ref = session.get_xenapi().VDI.get_by_uuid(os_vdi_uuid) primary_name_label = get_name_label_for_image(image) session.get_xenapi().VDI.set_name_label(vdi_ref, primary_name_label) + cls._check_vdi_size(context, session, instance, os_vdi_uuid) return vdis @classmethod - def _fetch_image_glance_disk(cls, context, session, instance_id, image, + def _get_vdi_chain_size(cls, context, session, vdi_uuid): + """Compute the total size of a VDI chain, starting with the specified + VDI UUID. + + This will walk the VDI chain to the root, add the size of each VDI into + the total. + """ + size_bytes = 0 + for vdi_rec in walk_vdi_chain(session, vdi_uuid): + vdi_size_bytes = int(vdi_rec['physical_utilisation']) + LOG.debug(_('vdi_uuid=%(vdi_uuid)s vdi_size_bytes=' + '%(vdi_size_bytes)d' % locals())) + size_bytes += vdi_size_bytes + return size_bytes + + @classmethod + def _check_vdi_size(cls, context, session, instance, vdi_uuid): + size_bytes = cls._get_vdi_chain_size(context, session, vdi_uuid) + + # FIXME(sirp): this was copied directly from compute.manager.py, let's + # refactor this to a common area + instance_type_id = instance['instance_type_id'] + instance_type = db.instance_type_get(context, + instance_type_id) + allowed_size_gb = instance_type['local_gb'] + allowed_size_bytes = allowed_size_gb * 1024 * 1024 * 1024 + + LOG.debug(_("image_size_bytes=%(size_bytes)d, allowed_size_bytes=" + "%(allowed_size_bytes)d") % locals()) + + if size_bytes > allowed_size_bytes: + LOG.info(_("Image size %(size_bytes)d exceeded" + " instance_type allowed size " + "%(allowed_size_bytes)d") + % locals()) + raise exception.ImageTooLarge() + + @classmethod + def _fetch_image_glance_disk(cls, context, session, instance, image, image_type): """Fetch the image from Glance @@ -444,6 +486,7 @@ class VMHelper(HelperBase): Returns: A single filename if image_type is KERNEL_RAMDISK A list of dictionaries that describe VDIs, otherwise """ + instance_id = instance.id # FIXME(sirp): Since the Glance plugin seems to be required for the # VHD disk, it may be worth using the plugin for both VHD and RAW and # DISK restores @@ -750,6 +793,21 @@ def get_vhd_parent_uuid(session, vdi_ref): return None +def walk_vdi_chain(session, vdi_uuid): + """Yield vdi_recs for each element in a VDI chain""" + # TODO: perhaps make get_vhd_parent use this + while True: + vdi_ref = session.get_xenapi().VDI.get_by_uuid(vdi_uuid) + vdi_rec = session.get_xenapi().VDI.get_record(vdi_ref) + yield vdi_rec + + parent_uuid = vdi_rec['sm_config'].get('vhd-parent') + if parent_uuid: + vdi_uuid = parent_uuid + else: + break + + def wait_for_vhd_coalesce(session, instance_id, sr_ref, vdi_ref, original_parent_uuid): """ Spin until the parent VHD is coalesced into its parent VHD diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py index b1522729a..7586ade7b 100644 --- a/nova/virt/xenapi/vmops.py +++ b/nova/virt/xenapi/vmops.py @@ -137,7 +137,7 @@ class VMOps(object): def _create_disks(self, context, instance): disk_image_type = VMHelper.determine_disk_image_type(instance) vdis = VMHelper.fetch_image(context, self._session, - instance.id, instance.image_ref, + instance, instance.image_ref, instance.user_id, instance.project_id, disk_image_type) return vdis @@ -182,11 +182,11 @@ class VMOps(object): try: if instance.kernel_id: kernel = VMHelper.fetch_image(context, self._session, - instance.id, instance.kernel_id, instance.user_id, + instance, instance.kernel_id, instance.user_id, instance.project_id, ImageType.KERNEL)[0] if instance.ramdisk_id: ramdisk = VMHelper.fetch_image(context, self._session, - instance.id, instance.ramdisk_id, instance.user_id, + instance, instance.ramdisk_id, instance.user_id, instance.project_id, ImageType.RAMDISK)[0] # Create the VM ref and attach the first disk first_vdi_ref = self._session.call_xenapi('VDI.get_by_uuid', -- cgit From b238bcd9de989e7dabe6698b3de77a104d96a941 Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Fri, 12 Aug 2011 16:25:16 -0500 Subject: Updated logging. --- nova/virt/xenapi/vm_utils.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index e86f72347..1f5f9b416 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -445,8 +445,9 @@ class VMHelper(HelperBase): """ size_bytes = 0 for vdi_rec in walk_vdi_chain(session, vdi_uuid): + cur_vdi_uuid = vdi_rec['uuid'] vdi_size_bytes = int(vdi_rec['physical_utilisation']) - LOG.debug(_('vdi_uuid=%(vdi_uuid)s vdi_size_bytes=' + LOG.debug(_('vdi_uuid=%(cur_vdi_uuid)s vdi_size_bytes=' '%(vdi_size_bytes)d' % locals())) size_bytes += vdi_size_bytes return size_bytes -- cgit From 1f3eb69ec547737447e91116881a8cb85157d65c Mon Sep 17 00:00:00 2001 From: Dan Wendlandt Date: Fri, 12 Aug 2011 18:16:07 -0700 Subject: fix issue introduced in merge --- nova/network/linux_net.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index be4269392..904014716 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -463,7 +463,7 @@ def initialize_gateway_device(dev, network_ref): # NOTE(vish): The ip for dnsmasq has to be the first address on the # bridge for it to respond to reqests properly - suffix = net_attrs['cidr'].rpartition('/')[2] + suffix = network_ref['cidr'].rpartition('/')[2] out, err = _execute('ip', 'addr', 'add', '%s/%s' % (network_ref['dhcp_server'], suffix), @@ -477,7 +477,7 @@ def initialize_gateway_device(dev, network_ref): raise exception.Error('Failed to add ip: %s' % err) if(FLAGS.use_ipv6): _execute('ip', '-f', 'inet6', 'addr', - 'change', net_attrs['cidr_v6'], + 'change', network_ref['cidr_v6'], 'dev', dev, run_as_root=True) # NOTE(vish): If the public interface is the same as the # bridge, then the bridge has to be in promiscuous -- cgit From e0e49dd7340bbb26c82f18a94a6582a5684925fa Mon Sep 17 00:00:00 2001 From: Dan Wendlandt Date: Sat, 13 Aug 2011 18:58:29 -0700 Subject: have NetworkManager generate MAC address and pass it to the driver for plugging. Sets the stage for being able to do duplicate checks on those MACs as well. --- nova/network/linux_net.py | 68 ++++++++++++++++++++++++++++------------------- nova/network/manager.py | 18 ++++++++++--- nova/utils.py | 9 ------- 3 files changed, 54 insertions(+), 41 deletions(-) diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index 904014716..248f1ce5a 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -458,13 +458,13 @@ def floating_forward_rules(floating_ip, fixed_ip): def initialize_gateway_device(dev, network_ref): - if not network_ref: - return + if not network_ref: + return - # NOTE(vish): The ip for dnsmasq has to be the first address on the - # bridge for it to respond to reqests properly - suffix = network_ref['cidr'].rpartition('/')[2] - out, err = _execute('ip', 'addr', 'add', + # NOTE(vish): The ip for dnsmasq has to be the first address on the + # bridge for it to respond to reqests properly + suffix = network_ref['cidr'].rpartition('/')[2] + out, err = _execute('ip', 'addr', 'add', '%s/%s' % (network_ref['dhcp_server'], suffix), 'brd', @@ -473,19 +473,18 @@ def initialize_gateway_device(dev, network_ref): dev, run_as_root=True, check_exit_code=False) - if err and err != 'RTNETLINK answers: File exists\n': - raise exception.Error('Failed to add ip: %s' % err) - if(FLAGS.use_ipv6): - _execute('ip', '-f', 'inet6', 'addr', + if err and err != 'RTNETLINK answers: File exists\n': + raise exception.Error('Failed to add ip: %s' % err) + if(FLAGS.use_ipv6): + _execute('ip', '-f', 'inet6', 'addr', 'change', network_ref['cidr_v6'], 'dev', dev, run_as_root=True) - # NOTE(vish): If the public interface is the same as the - # bridge, then the bridge has to be in promiscuous - # to forward packets properly. - if(FLAGS.public_interface == dev): - _execute('ip', 'link', 'set', + # NOTE(vish): If the public interface is the same as the + # bridge, then the bridge has to be in promiscuous + # to forward packets properly. + if(FLAGS.public_interface == dev): + _execute('ip', 'link', 'set', 'dev', dev, 'promisc', 'on', run_as_root=True) - _execute('ip', 'link', 'set', dev, 'up', run_as_root=True) def get_dhcp_leases(context, network_ref): @@ -718,6 +717,7 @@ def _ip_bridge_cmd(action, params, device): cmd.extend(['dev', device]) return cmd + # Similar to compute virt layers, the Linux network node # code uses a flexible driver model to support different ways # of creating ethernet interfaces and attaching them to the network. @@ -725,8 +725,8 @@ def _ip_bridge_cmd(action, params, device): # act as gateway/dhcp/vpn/etc. endpoints not VM interfaces. -def plug(network): - return interface_driver.plug(network) +def plug(network, mac_address): + return interface_driver.plug(network, mac_address) def unplug(network): @@ -737,7 +737,7 @@ class LinuxNetInterfaceDriver(object): """Abstract class that defines generic network host API""" """ for for all Linux interface drivers.""" - def plug(self, network): + def plug(self, network, mac_address): """Create Linux device, return device name""" raise NotImplementedError() @@ -749,13 +749,14 @@ class LinuxNetInterfaceDriver(object): # plugs interfaces using Linux Bridge class LinuxBridgeInterfaceDriver(LinuxNetInterfaceDriver): - def plug(self, network): + def plug(self, network, mac_address): if network.get('vlan', None) is not None: LinuxBridgeInterfaceDriver.ensure_vlan_bridge( network['vlan'], network['bridge'], network['bridge_interface'], - network) + network, + mac_address) else: LinuxBridgeInterfaceDriver.ensure_bridge( network['bridge'], @@ -769,16 +770,16 @@ class LinuxBridgeInterfaceDriver(LinuxNetInterfaceDriver): @classmethod def ensure_vlan_bridge(_self, vlan_num, bridge, bridge_interface, - net_attrs=None): + net_attrs=None, mac_address=None): """Create a vlan and bridge unless they already exist.""" interface = LinuxBridgeInterfaceDriver.ensure_vlan(vlan_num, - bridge_interface) + bridge_interface, mac_address) LinuxBridgeInterfaceDriver.ensure_bridge(bridge, interface, net_attrs) return interface @classmethod @utils.synchronized('ensure_vlan', external=True) - def ensure_vlan(_self, vlan_num, bridge_interface): + def ensure_vlan(_self, vlan_num, bridge_interface, mac_address=None): """Create a vlan unless it already exists.""" interface = 'vlan%s' % vlan_num if not _device_exists(interface): @@ -787,6 +788,11 @@ class LinuxBridgeInterfaceDriver(LinuxNetInterfaceDriver): 'VLAN_PLUS_VID_NO_PAD', run_as_root=True) _execute('vconfig', 'add', bridge_interface, vlan_num, run_as_root=True) + # (danwent) the bridge will inherit this address, so we want to + # make sure it is the value set from the NetworkManager + if mac_address: + _execute('ip', 'link', 'set', interface, "address", + mac_address, run_as_root=True) _execute('ip', 'link', 'set', interface, 'up', run_as_root=True) return interface @@ -812,6 +818,11 @@ class LinuxBridgeInterfaceDriver(LinuxNetInterfaceDriver): _execute('brctl', 'setfd', bridge, 0, run_as_root=True) # _execute('brctl setageing %s 10' % bridge, run_as_root=True) _execute('brctl', 'stp', bridge, 'off', run_as_root=True) + # (danwent) bridge device MAC address can't be set directly. + # instead it inherits the MAC address of the first device on the + # bridge, which will either be the vlan interface, or a + # physical NIC. + _execute('ip', 'link', 'set', bridge, 'up', run_as_root=True) if interface: out, err = _execute('brctl', 'addif', bridge, interface, @@ -856,11 +867,10 @@ class LinuxBridgeInterfaceDriver(LinuxNetInterfaceDriver): # plugs interfaces using Open vSwitch class LinuxOVSInterfaceDriver(LinuxNetInterfaceDriver): - def plug(self, network): + def plug(self, network, mac_address): dev = "gw-" + str(network['id']) if not _device_exists(dev): bridge = FLAGS.linuxnet_ovs_integration_bridge - mac_addr = utils.generate_mac_address() _execute('ovs-vsctl', '--', '--may-exist', 'add-port', bridge, dev, '--', 'set', 'Interface', dev, "type=internal", @@ -869,10 +879,12 @@ class LinuxOVSInterfaceDriver(LinuxNetInterfaceDriver): '--', 'set', 'Interface', dev, "external-ids:iface-status=active", '--', 'set', 'Interface', dev, - "external-ids:attached-mac=%s" % mac_addr, + "external-ids:attached-mac=%s" % mac_address, run_as_root=True) - _execute('ip', 'link', 'set', dev, "address", mac_addr, + _execute('ip', 'link', 'set', dev, "address", mac_address, run_as_root=True) + _execute('ip', 'link', 'set', dev, 'up', run_as_root=True) + return dev def unplug(self, network): diff --git a/nova/network/manager.py b/nova/network/manager.py index 653d116fb..1ad48fbc1 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -510,7 +510,7 @@ class NetworkManager(manager.SchedulerDependentManager): def _allocate_mac_addresses(self, context, instance_id, networks): """Generates mac addresses and creates vif rows in db for them.""" for network in networks: - vif = {'address': utils.generate_mac_address(), + vif = {'address': self.generate_mac_address(), 'instance_id': instance_id, 'network_id': network['id']} # try FLAG times to create a vif record with a unique mac_address @@ -519,12 +519,20 @@ class NetworkManager(manager.SchedulerDependentManager): self.db.virtual_interface_create(context, vif) break except exception.VirtualInterfaceCreateException: - vif['address'] = utils.generate_mac_address() + vif['address'] = self.generate_mac_address() else: self.db.virtual_interface_delete_by_instance(context, instance_id) raise exception.VirtualInterfaceMacAddressException() + def generate_mac_address(self): + """Generate an Ethernet MAC address.""" + mac = [0x02, 0x16, 0x3e, + random.randint(0x00, 0x7f), + random.randint(0x00, 0xff), + random.randint(0x00, 0xff)] + return ':'.join(map(lambda x: "%02x" % x, mac)) + def add_fixed_ip_to_instance(self, context, instance_id, host, network_id): """Adds a fixed ip to an instance from specified network.""" networks = [self.db.network_get(context, network_id)] @@ -796,7 +804,8 @@ class FlatDHCPManager(FloatingIP, RPCAllocateFixedIP, NetworkManager): """Sets up network on this host.""" network_ref['dhcp_server'] = self._get_dhcp_ip(context, network_ref) - dev = self.driver.plug(network_ref) + mac_address = self.generate_mac_address() + dev = self.driver.plug(network_ref, mac_address) self.driver.initialize_gateway_device(dev, network_ref) if not FLAGS.fake_network: @@ -897,7 +906,8 @@ class VlanManager(RPCAllocateFixedIP, FloatingIP, NetworkManager): address = network_ref['vpn_public_address'] network_ref['dhcp_server'] = self._get_dhcp_ip(context, network_ref) - dev = self.driver.plug(network_ref) + mac_address = self.generate_mac_address() + dev = self.driver.plug(network_ref, mac_address) self.driver.initialize_gateway_device(dev, network_ref) # NOTE(vish): only ensure this forward if the address hasn't been set diff --git a/nova/utils.py b/nova/utils.py index 46d60c735..7276b6bd5 100644 --- a/nova/utils.py +++ b/nova/utils.py @@ -338,15 +338,6 @@ def get_my_linklocal(interface): " :%(ex)s") % locals()) -def generate_mac_address(): - """Generate an Ethernet MAC address.""" - mac = [0x02, 0x16, 0x3e, - random.randint(0x00, 0x7f), - random.randint(0x00, 0xff), - random.randint(0x00, 0xff)] - return ':'.join(map(lambda x: "%02x" % x, mac)) - - def utcnow(): """Overridable version of utils.utcnow.""" if utcnow.override_time: -- cgit From 53ca062830639de242d2cadda4f6bf473d4b6b62 Mon Sep 17 00:00:00 2001 From: Dan Wendlandt Date: Sun, 14 Aug 2011 20:05:18 -0700 Subject: fix missing 'run_as_root' from bad merge --- nova/network/linux_net.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index 248f1ce5a..57c1d0c28 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -846,8 +846,10 @@ class LinuxBridgeInterfaceDriver(LinuxNetInterfaceDriver): fields = line.split() if fields and fields[0] == 'inet': params = fields[1:-1] - _execute(*_ip_bridge_cmd('del', params, fields[-1])) - _execute(*_ip_bridge_cmd('add', params, bridge)) + _execute(*_ip_bridge_cmd('del', params, fields[-1]), + run_as_root=True) + _execute(*_ip_bridge_cmd('add', params, bridge), + run_as_root=True) if gateway: _execute('route', 'add', 'default', 'gw', gateway, run_as_root=True) -- cgit From 1a2bc77871af060069ba0de80637198be78f8169 Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Mon, 15 Aug 2011 09:47:01 -0500 Subject: Fixed merge conflict. --- nova/virt/xenapi/vm_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index bf3bf4610..b6f471c0f 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -414,7 +414,7 @@ class VMHelper(HelperBase): return vdi_ref @classmethod - def fetch_image(cls, context, session, instance_id, image, user_id, + def fetch_image(cls, context, session, instance, image, user_id, project_id, image_type): """Fetch image from glance based on image type. -- cgit From bbd577de616915025e524e330f1991f3f155388c Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Mon, 15 Aug 2011 13:35:53 -0500 Subject: Corrected names in TODO/FIXME. --- nova/virt/xenapi/vm_utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py index b6f471c0f..4a1f07bb1 100644 --- a/nova/virt/xenapi/vm_utils.py +++ b/nova/virt/xenapi/vm_utils.py @@ -501,7 +501,7 @@ class VMHelper(HelperBase): def _check_vdi_size(cls, context, session, instance, vdi_uuid): size_bytes = cls._get_vdi_chain_size(context, session, vdi_uuid) - # FIXME(sirp): this was copied directly from compute.manager.py, let's + # FIXME(jk0): this was copied directly from compute.manager.py, let's # refactor this to a common area instance_type_id = instance['instance_type_id'] instance_type = db.instance_type_get(context, @@ -853,7 +853,7 @@ def get_vhd_parent_uuid(session, vdi_ref): def walk_vdi_chain(session, vdi_uuid): """Yield vdi_recs for each element in a VDI chain""" - # TODO: perhaps make get_vhd_parent use this + # TODO(jk0): perhaps make get_vhd_parent use this while True: vdi_ref = session.get_xenapi().VDI.get_by_uuid(vdi_uuid) vdi_rec = session.get_xenapi().VDI.get_record(vdi_ref) -- cgit From 54ba0d6d25f60b5acb363d141aeba63e4c727c72 Mon Sep 17 00:00:00 2001 From: Jason Koelker Date: Mon, 15 Aug 2011 15:28:28 -0500 Subject: fix typo where I forgot a comma --- nova/network/manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/network/manager.py b/nova/network/manager.py index 1513dd473..31bb29f81 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -671,7 +671,7 @@ class NetworkManager(manager.SchedulerDependentManager): if used_subnet in subnet: msg = _('requested cidr (%{cidr}) conflicts with ' 'existing smaller cidr (%{smaller})') - raise ValueError(msg % {'cidr': subnet + raise ValueError(msg % {'cidr': subnet, 'smaller': used_subnet}) -- cgit From d3e2be67d116dcdbe0484a10708ae00d040d2d9f Mon Sep 17 00:00:00 2001 From: Jason Koelker Date: Mon, 15 Aug 2011 15:29:55 -0500 Subject: i hate these exceptions where it should just return an empty list --- nova/network/manager.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/nova/network/manager.py b/nova/network/manager.py index 31bb29f81..ddb60ecfa 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -656,7 +656,10 @@ class NetworkManager(manager.SchedulerDependentManager): subnets_v4 = list(fixed_net_v4.subnet(prefixlen_v4, count=num_networks)) - nets = self.db.network_get_all(context) + try: + nets = self.db.network_get_all(context) + except exception.NoNetworksFound: + nets = [] used_subnets = [netaddr.IPNetwork(net['cidr']) for net in nets] for subnet in subnets_v4: -- cgit From 5276b4981dc6e8c4f3d4b9c733939290df3c6a72 Mon Sep 17 00:00:00 2001 From: Jason Koelker Date: Mon, 15 Aug 2011 15:45:14 -0500 Subject: pep8 fix --- nova/network/manager.py | 1 - 1 file changed, 1 deletion(-) diff --git a/nova/network/manager.py b/nova/network/manager.py index ddb60ecfa..6ba73300e 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -677,7 +677,6 @@ class NetworkManager(manager.SchedulerDependentManager): raise ValueError(msg % {'cidr': subnet, 'smaller': used_subnet}) - subnets = itertools.izip_longest(subnets_v4, subnets_v6) for index, (subnet_v4, subnet_v6) in enumerate(subnets): net = {} -- cgit From 945a874a77c63710f57fa31988ba7f9ba65a5ad0 Mon Sep 17 00:00:00 2001 From: Jason Koelker Date: Mon, 15 Aug 2011 15:47:11 -0500 Subject: return the created networks --- nova/network/manager.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/nova/network/manager.py b/nova/network/manager.py index 6ba73300e..7748479e9 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -677,6 +677,7 @@ class NetworkManager(manager.SchedulerDependentManager): raise ValueError(msg % {'cidr': subnet, 'smaller': used_subnet}) + networks = [] subnets = itertools.izip_longest(subnets_v4, subnets_v6) for index, (subnet_v4, subnet_v6) in enumerate(subnets): net = {} @@ -728,9 +729,12 @@ class NetworkManager(manager.SchedulerDependentManager): if not network: raise ValueError(_('Network already exists!')) + else: + networks.append(network) if network and cidr and subnet_v4: self._create_fixed_ips(context, network['id']) + return networks @property def _bottom_reserved_ips(self): # pylint: disable=R0201 -- cgit From a20e18c5ae2c77ed005e5dc9cec7b92d67e50a0b Mon Sep 17 00:00:00 2001 From: Jason Koelker Date: Mon, 15 Aug 2011 16:30:51 -0500 Subject: allow for finding a network that fits the size, also format string correctly --- nova/network/manager.py | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/nova/network/manager.py b/nova/network/manager.py index 7748479e9..7e9a0ecf6 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -662,18 +662,26 @@ class NetworkManager(manager.SchedulerDependentManager): nets = [] used_subnets = [netaddr.IPNetwork(net['cidr']) for net in nets] - for subnet in subnets_v4: + for subnet in list(subnets_v4): if subnet in used_subnets: - raise ValueError(_('cidr already in use')) + next_subnet = subnet.next() + while next_subnet in subnets_v4: + next_subnet = next_subnet.next() + if next_subnet in fixed_net_v4: + subnets_v4.remove(subnet) + subnets_v4.append(next_subnet) + subnet = next_subnet + else: + raise ValueError(_('cidr already in use')) for used_subnet in used_subnets: if subnet in used_subnet: - msg = _('requested cidr (%{cidr}) conflicts with ' - 'existing supernet (%{super})') + msg = _('requested cidr (%(cidr)s) conflicts with ' + 'existing supernet (%(super)s)') raise ValueError(msg % {'cidr': subnet, 'super': used_subnet}) if used_subnet in subnet: - msg = _('requested cidr (%{cidr}) conflicts with ' - 'existing smaller cidr (%{smaller})') + msg = _('requested cidr (%(cidr)s) conflicts with ' + 'existing smaller cidr (%(smaller)s)') raise ValueError(msg % {'cidr': subnet, 'smaller': used_subnet}) -- cgit From a34943e89e9aee0a26bd4fd03a2b12fc954029fd Mon Sep 17 00:00:00 2001 From: Jason Koelker Date: Mon, 15 Aug 2011 16:41:11 -0500 Subject: have the tests call create_networks directly --- nova/network/manager.py | 28 ++++++++++++------ nova/tests/test_network.py | 72 +++++++++++++++++++++++++++++----------------- 2 files changed, 65 insertions(+), 35 deletions(-) diff --git a/nova/network/manager.py b/nova/network/manager.py index 7e9a0ecf6..4954dc7e9 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -662,12 +662,17 @@ class NetworkManager(manager.SchedulerDependentManager): nets = [] used_subnets = [netaddr.IPNetwork(net['cidr']) for net in nets] + def find_next(subnet): + next_subnet = subnet.next() + while next_subnet in subnets_v4: + next_subnet = next_subnet.next() + if next_subnet in fixed_net_v4: + return next_subnet + for subnet in list(subnets_v4): if subnet in used_subnets: - next_subnet = subnet.next() - while next_subnet in subnets_v4: - next_subnet = next_subnet.next() - if next_subnet in fixed_net_v4: + next_subnet = find_next(subnet) + if next_subnet: subnets_v4.remove(subnet) subnets_v4.append(next_subnet) subnet = next_subnet @@ -680,10 +685,17 @@ class NetworkManager(manager.SchedulerDependentManager): raise ValueError(msg % {'cidr': subnet, 'super': used_subnet}) if used_subnet in subnet: - msg = _('requested cidr (%(cidr)s) conflicts with ' - 'existing smaller cidr (%(smaller)s)') - raise ValueError(msg % {'cidr': subnet, - 'smaller': used_subnet}) + next_subnet = find_next(subnet) + if next_subnet: + subnets_v4.remove(subnet) + subnets_v4.append(next_subnet) + subnet = next_subnet + else: + msg = _('requested cidr (%(cidr)s) conflicts ' + 'with existing smaller cidr ' + '(%(smaller)s)') + raise ValueError(msg % {'cidr': subnet, + 'smaller': used_subnet}) networks = [] subnets = itertools.izip_longest(subnets_v4, subnets_v6) diff --git a/nova/tests/test_network.py b/nova/tests/test_network.py index c673f5d06..0ead680ee 100644 --- a/nova/tests/test_network.py +++ b/nova/tests/test_network.py @@ -255,7 +255,7 @@ class CommonNetworkTestCase(test.TestCase): raise exception.NetworkNotFoundForCidr() def network_create_safe(self, context, net): - fakenet = {} + fakenet = dict(net) fakenet['id'] = 999 return fakenet @@ -269,6 +269,9 @@ class CommonNetworkTestCase(test.TestCase): def deallocate_fixed_ip(self, context, address): self.deallocate_called = address + def _create_fixed_ips(self, context, network_id): + pass + def fake_create_fixed_ips(self, context, network_id): return None @@ -286,16 +289,20 @@ class CommonNetworkTestCase(test.TestCase): def test_validate_cidrs(self): manager = self.FakeNetworkManager() - nets = manager._validate_cidrs(None, '192.168.0.0/24', 1, 256) + nets = manager.create_networks(None, 'fake', '192.168.0.0/24', + False, 1, 256, None, None, None, + None) self.assertEqual(1, len(nets)) - cidrs = [str(net) for net in nets] + cidrs = [str(net['cidr']) for net in nets] self.assertTrue('192.168.0.0/24' in cidrs) def test_validate_cidrs_split_exact_in_half(self): manager = self.FakeNetworkManager() - nets = manager._validate_cidrs(None, '192.168.0.0/24', 2, 128) + nets = manager.create_networks(None, 'fake', '192.168.0.0/24', + False, 2, 128, None, None, None, + None) self.assertEqual(2, len(nets)) - cidrs = [str(net) for net in nets] + cidrs = [str(net['cidr']) for net in nets] self.assertTrue('192.168.0.0/25' in cidrs) self.assertTrue('192.168.0.128/25' in cidrs) @@ -306,9 +313,11 @@ class CommonNetworkTestCase(test.TestCase): manager.db.network_get_all(ctxt).AndReturn([{'id': 1, 'cidr': '192.168.2.0/24'}]) self.mox.ReplayAll() - nets = manager._validate_cidrs(None, '192.168.0.0/16', 4, 256) + nets = manager.create_networks(None, 'fake', '192.168.0.0/16', + False, 4, 256, None, None, None, + None) self.assertEqual(4, len(nets)) - cidrs = [str(net) for net in nets] + cidrs = [str(net['cidr']) for net in nets] exp_cidrs = ['192.168.0.0/24', '192.168.1.0/24', '192.168.3.0/24', '192.168.4.0/24'] for exp_cidr in exp_cidrs: @@ -324,8 +333,9 @@ class CommonNetworkTestCase(test.TestCase): self.mox.ReplayAll() # ValueError: requested cidr (192.168.2.0/24) conflicts with # existing smaller cidr - args = [None, '192.168.2.0/24', 1, 256] - self.assertRaises(ValueError, manager._validate_cidrs, *args) + args = (None, 'fake', '192.168.2.0/24', False, 1, 256, None, None, + None, None) + self.assertRaises(ValueError, manager.create_networks, *args) def test_validate_cidrs_split_smaller_cidr_in_use(self): manager = self.FakeNetworkManager() @@ -334,9 +344,10 @@ class CommonNetworkTestCase(test.TestCase): manager.db.network_get_all(ctxt).AndReturn([{'id': 1, 'cidr': '192.168.2.0/25'}]) self.mox.ReplayAll() - nets = manager._validate_cidrs(None, '192.168.0.0/16', 4, 256) + nets = manager.create_networks(None, 'fake', '192.168.0.0/16', + False, 4, 256, None, None, None, None) self.assertEqual(4, len(nets)) - cidrs = [str(net) for net in nets] + cidrs = [str(net['cidr']) for net in nets] exp_cidrs = ['192.168.0.0/24', '192.168.1.0/24', '192.168.3.0/24', '192.168.4.0/24'] for exp_cidr in exp_cidrs: @@ -350,9 +361,10 @@ class CommonNetworkTestCase(test.TestCase): manager.db.network_get_all(ctxt).AndReturn([{'id': 1, 'cidr': '192.168.2.9/29'}]) self.mox.ReplayAll() - nets = manager._validate_cidrs(None, '192.168.2.0/24', 3, 32) + nets = manager.create_networks(None, 'fake', '192.168.2.0/24', + False, 3, 32, None, None, None, None) self.assertEqual(3, len(nets)) - cidrs = [str(net) for net in nets] + cidrs = [str(net['cidr']) for net in nets] exp_cidrs = ['192.168.2.32/27', '192.168.2.64/27', '192.168.2.96/27'] for exp_cidr in exp_cidrs: self.assertTrue(exp_cidr in cidrs) @@ -367,17 +379,19 @@ class CommonNetworkTestCase(test.TestCase): {'id': 3, 'cidr': '192.168.2.128/26'}] manager.db.network_get_all(ctxt).AndReturn(in_use) self.mox.ReplayAll() - args = [None, '192.168.2.0/24', 3, 64] + args = (None, 'fake', '192.168.2.0/24', False, 3, 64, None, None, + None, None) # ValueError: Not enough subnets avail to satisfy requested num_ # networks - some subnets in requested range already # in use - self.assertRaises(ValueError, manager._validate_cidrs, *args) + self.assertRaises(ValueError, manager.create_networks, *args) def test_validate_cidrs_one_in_use(self): manager = self.FakeNetworkManager() - args = [None, '192.168.0.0/24', 2, 256] + args = (None, 'fake', '192.168.0.0/24', False, 2, 256, None, None, + None, None) # ValueError: network_size * num_networks exceeds cidr size - self.assertRaises(ValueError, manager._validate_cidrs, *args) + self.assertRaises(ValueError, manager.create_networks, *args) def test_validate_cidrs_already_used(self): manager = self.FakeNetworkManager() @@ -387,20 +401,23 @@ class CommonNetworkTestCase(test.TestCase): 'cidr': '192.168.0.0/24'}]) self.mox.ReplayAll() # ValueError: cidr already in use - args = [None, '192.168.0.0/24', 1, 256] - self.assertRaises(ValueError, manager._validate_cidrs, *args) + args = (None, 'fake', '192.168.0.0/24', False, 1, 256, None, None, + None, None) + self.assertRaises(ValueError, manager.create_networks, *args) def test_validate_cidrs_too_many(self): manager = self.FakeNetworkManager() - args = [None, '192.168.0.0/24', 200, 256] + args = (None, 'fake', '192.168.0.0/24', False, 200, 256, None, None, + None, None) # ValueError: Not enough subnets avail to satisfy requested # num_networks - self.assertRaises(ValueError, manager._validate_cidrs, *args) + self.assertRaises(ValueError, manager.create_networks, *args) def test_validate_cidrs_split_partial(self): manager = self.FakeNetworkManager() - nets = manager._validate_cidrs(None, '192.168.0.0/16', 2, 256) - returned_cidrs = [str(net) for net in nets] + nets = manager.create_networks(None, 'fake', '192.168.0.0/16', + False, 2, 256, None, None, None, None) + returned_cidrs = [str(net['cidr']) for net in nets] self.assertTrue('192.168.0.0/24' in returned_cidrs) self.assertTrue('192.168.1.0/24' in returned_cidrs) @@ -411,10 +428,11 @@ class CommonNetworkTestCase(test.TestCase): fakecidr = [{'id': 1, 'cidr': '192.168.0.0/8'}] manager.db.network_get_all(ctxt).AndReturn(fakecidr) self.mox.ReplayAll() - args = [None, '192.168.0.0/24', 1, 256] + args = (None, 'fake', '192.168.0.0/24', False, 1, 256, None, None, + None, None) # ValueError: requested cidr (192.168.0.0/24) conflicts # with existing supernet - self.assertRaises(ValueError, manager._validate_cidrs, *args) + self.assertRaises(ValueError, manager.create_networks, *args) def test_create_networks(self): cidr = '192.168.0.0/24' @@ -424,7 +442,7 @@ class CommonNetworkTestCase(test.TestCase): args = [None, 'foo', cidr, None, 1, 256, 'fd00::/48', None, None, None] result = manager.create_networks(*args) - self.assertEqual(manager.create_networks(*args), None) + self.assertTrue(manager.create_networks(*args)) def test_create_networks_cidr_already_used(self): manager = self.FakeNetworkManager() @@ -444,4 +462,4 @@ class CommonNetworkTestCase(test.TestCase): self.fake_create_fixed_ips) args = [None, 'foo', cidr, None, 10, 256, 'fd00::/48', None, None, None] - self.assertEqual(manager.create_networks(*args), None) + self.assertTrue(manager.create_networks(*args)) -- cgit From cef63a6c2e8f3e1baa126ba41703aac81c2fc6ae Mon Sep 17 00:00:00 2001 From: Jason Koelker Date: Mon, 15 Aug 2011 16:46:30 -0500 Subject: add note --- nova/network/manager.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/nova/network/manager.py b/nova/network/manager.py index 4954dc7e9..a52dd1953 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -656,6 +656,8 @@ class NetworkManager(manager.SchedulerDependentManager): subnets_v4 = list(fixed_net_v4.subnet(prefixlen_v4, count=num_networks)) + # NOTE(jkoelker): This replaces the _validate_cidrs call and + # prevents looping multiple times try: nets = self.db.network_get_all(context) except exception.NoNetworksFound: -- cgit From 8b4805b24bb51adca501a38b4b7dbf730cc826d2 Mon Sep 17 00:00:00 2001 From: Jason Koelker Date: Mon, 15 Aug 2011 16:47:48 -0500 Subject: pep8 --- nova/compute/manager.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 16b8e14b4..4e84cb936 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -1315,7 +1315,8 @@ class ComputeManager(manager.SchedulerDependentManager): # This nwfilter is necessary on the destination host. # In addition, this method is creating filtering rule # onto destination host. - self.driver.ensure_filtering_rules_for_instance(instance_ref, network_info) + self.driver.ensure_filtering_rules_for_instance(instance_ref, + network_info) # Preparation for block migration if block_migration: -- cgit From e57e9e9bbc37fbe87052ccc66bf7b97501e1e759 Mon Sep 17 00:00:00 2001 From: Jason Koelker Date: Mon, 15 Aug 2011 16:48:28 -0500 Subject: pep8 --- nova/virt/libvirt/connection.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nova/virt/libvirt/connection.py b/nova/virt/libvirt/connection.py index 2b17e244a..6ad4328b1 100644 --- a/nova/virt/libvirt/connection.py +++ b/nova/virt/libvirt/connection.py @@ -1539,7 +1539,8 @@ class LibvirtConnection(driver.ComputeDriver): # basic-filtering must be set here. self.firewall_driver.setup_basic_filtering(instance_ref, network_info) # setting up n)ova-instance-instance-xx mainly. - self.firewall_driver.prepare_instance_filter(instance_ref, network_info) + self.firewall_driver.prepare_instance_filter(instance_ref, + network_info) # wait for completion timeout_count = range(FLAGS.live_migration_retry_count) -- cgit From 8d83ceb9f8baef3c768c4fc087afb89188250c26 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Mon, 15 Aug 2011 16:29:52 -0700 Subject: fix error logging in s3.py --- nova/image/s3.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/nova/image/s3.py b/nova/image/s3.py index ccbfa89cd..e5008d856 100644 --- a/nova/image/s3.py +++ b/nova/image/s3.py @@ -193,6 +193,8 @@ class S3ImageService(service.BaseImageService): def delayed_create(): """This handles the fetching and decrypting of the part files.""" + log_vars = {'image_location': image_location, + 'image_path': image_path} metadata['properties']['image_state'] = 'downloading' self.service.update(context, image_id, metadata) @@ -214,7 +216,7 @@ class S3ImageService(service.BaseImageService): except Exception: LOG.error(_("Failed to download %(image_location)s " - "to %(image_path)s"), locals()) + "to %(image_path)s"), log_vars) metadata['properties']['image_state'] = 'failed_download' self.service.update(context, image_id, metadata) raise @@ -238,7 +240,7 @@ class S3ImageService(service.BaseImageService): dec_filename) except Exception: LOG.error(_("Failed to decrypt %(image_location)s " - "to %(image_path)s"), locals()) + "to %(image_path)s"), log_vars) metadata['properties']['image_state'] = 'failed_decrypt' self.service.update(context, image_id, metadata) raise @@ -250,7 +252,7 @@ class S3ImageService(service.BaseImageService): unz_filename = self._untarzip_image(image_path, dec_filename) except Exception: LOG.error(_("Failed to untar %(image_location)s " - "to %(image_path)s"), locals()) + "to %(image_path)s"), log_vars) metadata['properties']['image_state'] = 'failed_untar' self.service.update(context, image_id, metadata) raise @@ -263,7 +265,7 @@ class S3ImageService(service.BaseImageService): metadata, image_file) except Exception: LOG.error(_("Failed to upload %(image_location)s " - "to %(image_path)s"), locals()) + "to %(image_path)s"), log_vars) metadata['properties']['image_state'] = 'failed_upload' self.service.update(context, image_id, metadata) raise -- cgit From c4f6df55fa8a9c0746074c814b510e4a4cd4e512 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Mon, 15 Aug 2011 17:12:03 -0700 Subject: log the full exception so we don't lose traceback through eventlet --- nova/image/s3.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/nova/image/s3.py b/nova/image/s3.py index e5008d856..abf01a942 100644 --- a/nova/image/s3.py +++ b/nova/image/s3.py @@ -215,11 +215,11 @@ class S3ImageService(service.BaseImageService): shutil.copyfileobj(part, combined) except Exception: - LOG.error(_("Failed to download %(image_location)s " - "to %(image_path)s"), log_vars) + LOG.exception(_("Failed to download %(image_location)s " + "to %(image_path)s"), log_vars) metadata['properties']['image_state'] = 'failed_download' self.service.update(context, image_id, metadata) - raise + return metadata['properties']['image_state'] = 'decrypting' self.service.update(context, image_id, metadata) @@ -239,11 +239,11 @@ class S3ImageService(service.BaseImageService): encrypted_iv, cloud_pk, dec_filename) except Exception: - LOG.error(_("Failed to decrypt %(image_location)s " - "to %(image_path)s"), log_vars) + LOG.exception(_("Failed to decrypt %(image_location)s " + "to %(image_path)s"), log_vars) metadata['properties']['image_state'] = 'failed_decrypt' self.service.update(context, image_id, metadata) - raise + return metadata['properties']['image_state'] = 'untarring' self.service.update(context, image_id, metadata) @@ -251,11 +251,11 @@ class S3ImageService(service.BaseImageService): try: unz_filename = self._untarzip_image(image_path, dec_filename) except Exception: - LOG.error(_("Failed to untar %(image_location)s " - "to %(image_path)s"), log_vars) + LOG.exception(_("Failed to untar %(image_location)s " + "to %(image_path)s"), log_vars) metadata['properties']['image_state'] = 'failed_untar' self.service.update(context, image_id, metadata) - raise + return metadata['properties']['image_state'] = 'uploading' self.service.update(context, image_id, metadata) @@ -264,11 +264,11 @@ class S3ImageService(service.BaseImageService): self.service.update(context, image_id, metadata, image_file) except Exception: - LOG.error(_("Failed to upload %(image_location)s " - "to %(image_path)s"), log_vars) + LOG.exception(_("Failed to upload %(image_location)s " + "to %(image_path)s"), log_vars) metadata['properties']['image_state'] = 'failed_upload' self.service.update(context, image_id, metadata) - raise + return metadata['properties']['image_state'] = 'available' metadata['status'] = 'active' -- cgit From 0801dee7b05463b40bf66ee5911c92ac5e4aabc8 Mon Sep 17 00:00:00 2001 From: Dan Prince Date: Tue, 16 Aug 2011 10:49:26 -0400 Subject: Fix test_metadata tests. --- nova/api/ec2/cloud.py | 2 +- nova/tests/test_metadata.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 152cd6887..9aebf92e3 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -305,7 +305,7 @@ class CloudController(object): 'hostname': hostname, 'instance-action': 'none', 'instance-id': ec2_id, - 'instance-type': instance_ref['instance_type'].name, + 'instance-type': instance_ref['instance_type']['name'], 'local-hostname': hostname, 'local-ipv4': address, 'placement': {'availability-zone': availability_zone}, diff --git a/nova/tests/test_metadata.py b/nova/tests/test_metadata.py index ad678714e..bfc7a6d44 100644 --- a/nova/tests/test_metadata.py +++ b/nova/tests/test_metadata.py @@ -39,7 +39,7 @@ class MetadataTestCase(test.TestCase): 'key_name': None, 'host': 'test', 'launch_index': 1, - 'instance_type': 'm1.tiny', + 'instance_type': {'name': 'm1.tiny'}, 'reservation_id': 'r-xxxxxxxx', 'user_data': '', 'image_ref': 7, -- cgit From 44a278bc5a456c8eda74c61aaa68cfd74ee0d6e8 Mon Sep 17 00:00:00 2001 From: Brian Lamar Date: Tue, 16 Aug 2011 11:31:29 -0400 Subject: Small bug fix...don't cast DB objects to dicts. --- nova/api/openstack/views/servers.py | 4 ++-- nova/tests/integrated/test_servers.py | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/nova/api/openstack/views/servers.py b/nova/api/openstack/views/servers.py index 8222f6766..60fdf54be 100644 --- a/nova/api/openstack/views/servers.py +++ b/nova/api/openstack/views/servers.py @@ -146,7 +146,7 @@ class ViewBuilderV11(ViewBuilder): return response def _build_image(self, response, inst): - if 'image_ref' in dict(inst): + if inst.get("image_ref", None): image_href = inst['image_ref'] image_id = str(common.get_id_from_href(image_href)) _bookmark = self.image_builder.generate_bookmark(image_id) @@ -161,7 +161,7 @@ class ViewBuilderV11(ViewBuilder): } def _build_flavor(self, response, inst): - if "instance_type" in dict(inst): + if inst.get("instance_type", None): flavor_id = inst["instance_type"]['flavorid'] flavor_ref = self.flavor_builder.generate_href(flavor_id) flavor_bookmark = self.flavor_builder.generate_bookmark(flavor_id) diff --git a/nova/tests/integrated/test_servers.py b/nova/tests/integrated/test_servers.py index 150279a95..725f6d529 100644 --- a/nova/tests/integrated/test_servers.py +++ b/nova/tests/integrated/test_servers.py @@ -27,6 +27,7 @@ LOG = logging.getLogger('nova.tests.integrated') class ServersTest(integrated_helpers._IntegratedTestBase): + def test_get_servers(self): """Simple check that listing servers works.""" servers = self.api.get_servers() @@ -103,6 +104,10 @@ class ServersTest(integrated_helpers._IntegratedTestBase): # It should be available... # TODO(justinsb): Mock doesn't yet do this... #self.assertEqual('available', found_server['status']) + servers = self.api.get_servers(detail=True) + for server in servers: + self.assertTrue("image" in server) + self.assertTrue("flavor" in server) self._delete_server(created_server_id) -- cgit From 6220c4276e30c633ffc4165ce6db0d120c0e88a7 Mon Sep 17 00:00:00 2001 From: Jake Dahn Date: Tue, 16 Aug 2011 09:13:10 -0700 Subject: another trunk merge... a new change made it into nova before the code was merged --- nova/api/openstack/contrib/quotas.py | 107 ++++++++++++++++ nova/tests/api/openstack/contrib/test_quotas.py | 158 ++++++++++++++++++++++++ nova/tests/api/openstack/test_extensions.py | 1 + 3 files changed, 266 insertions(+) create mode 100644 nova/api/openstack/contrib/quotas.py create mode 100644 nova/tests/api/openstack/contrib/test_quotas.py diff --git a/nova/api/openstack/contrib/quotas.py b/nova/api/openstack/contrib/quotas.py new file mode 100644 index 000000000..7dbafb79a --- /dev/null +++ b/nova/api/openstack/contrib/quotas.py @@ -0,0 +1,107 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011 OpenStack LLC. +# 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 urlparse + +from nova import db +from nova import exception +from nova import quota +from nova.auth import manager as auth_manager +from nova.api.openstack import extensions + + +class QuotasController(object): + + def _format_quota_set(self, project_id, quota_set): + """Convert the quota object to a result dict""" + + return {'quota_set': { + 'id': str(project_id), + 'metadata_items': quota_set['metadata_items'], + 'injected_file_content_bytes': + quota_set['injected_file_content_bytes'], + 'volumes': quota_set['volumes'], + 'gigabytes': quota_set['gigabytes'], + 'ram': quota_set['ram'], + 'floating_ips': quota_set['floating_ips'], + 'instances': quota_set['instances'], + 'injected_files': quota_set['injected_files'], + 'cores': quota_set['cores'], + }} + + def index(self, req): + # NOTE(jakedahn): If http param defaults is true, list system defaults. + if urlparse.parse_qs(req.environ['QUERY_STRING']).get('defaults', + False): + return {'quota_set_list': [self._format_quota_set('__defaults__', + quota._get_default_quotas())]} + else: + context = req.environ['nova.context'] + user = req.environ.get('user') + projects = auth_manager.AuthManager().get_projects(user=user) + + quota_set_list = [self._format_quota_set(project.name, + quota.get_project_quotas(context, project.name)) + for project in projects] + return {'quota_set_list': quota_set_list} + + def show(self, req, id): + context = req.environ['nova.context'] + return self._format_quota_set(id, quota.get_project_quotas(context, + id)) + + def update(self, req, id, body): + context = req.environ['nova.context'] + project_id = id + resources = ['metadata_items', 'injected_file_content_bytes', + 'volumes', 'gigabytes', 'ram', 'floating_ips', 'instances', + 'injected_files', 'cores'] + + for key in body['quota_set'].keys(): + if key in resources: + value = int(body['quota_set'][key]) + try: + db.quota_update(context, project_id, key, value) + except exception.ProjectQuotaNotFound: + db.quota_create(context, project_id, key, value) + return {'quota_set': quota.get_project_quotas(context, project_id)} + + +class Quotas(extensions.ExtensionDescriptor): + + def get_name(self): + return "Quotas" + + def get_alias(self): + return "os-quotas" + + def get_description(self): + return "Quotas management support" + + def get_namespace(self): + return "http://docs.openstack.org/ext/quotas/api/v1.1" + + def get_updated(self): + return "2011-08-08T00:00:00+00:00" + + def get_resources(self): + resources = [] + + res = extensions.ResourceExtension('os-quotas', QuotasController()) + resources.append(res) + + return resources diff --git a/nova/tests/api/openstack/contrib/test_quotas.py b/nova/tests/api/openstack/contrib/test_quotas.py new file mode 100644 index 000000000..6ab2faf4d --- /dev/null +++ b/nova/tests/api/openstack/contrib/test_quotas.py @@ -0,0 +1,158 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011 OpenStack LLC. +# 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 json +import webob + +from nova import context +from nova import test +from nova.auth import manager as auth_manager +from nova.tests.api.openstack import fakes + + +from nova.api.openstack.contrib.quotas import QuotasController + + +def quota_set(id): + return {'quota_set': {'id': id, 'metadata_items': 128, 'volumes': 10, + 'gigabytes': 1000, 'ram': 51200, 'floating_ips': 10, + 'instances': 10, 'injected_files': 5, 'cores': 20, + 'injected_file_content_bytes': 10240}} + + +def quota_set_list(): + return {'quota_set_list': [quota_set('1234'), quota_set('5678'), + quota_set('update_me')]} + + +def create_project(project_name, manager_user): + auth_manager.AuthManager().create_project(project_name, manager_user) + + +def delete_project(project_name): + auth_manager.AuthManager().delete_project(project_name) + + +def create_admin_user(name): + auth_manager.AuthManager().create_user(name, admin=True) + + +def delete_user(name): + auth_manager.AuthManager().delete_user(name) + + +class QuotasTest(test.TestCase): + + def setUp(self): + super(QuotasTest, self).setUp() + self.controller = QuotasController() + self.context = context.get_admin_context() + + create_admin_user('foo') + create_project('1234', 'foo') + create_project('5678', 'foo') + create_project('update_me', 'foo') + + def tearDown(self): + delete_project('1234') + delete_project('5678') + delete_project('update_me') + delete_user('foo') + + def test_format_quota_set(self): + raw_quota_set = { + 'instances': 10, + 'cores': 20, + 'ram': 51200, + 'volumes': 10, + 'gigabytes': 1000, + 'floating_ips': 10, + 'metadata_items': 128, + 'injected_files': 5, + 'injected_file_content_bytes': 10240, + } + + quota_set = QuotasController()._format_quota_set('1234', raw_quota_set) + quota_set_check = quota_set['quota_set'] + + self.assertEqual(quota_set_check['id'], '1234') + self.assertEqual(quota_set_check['instances'], 10) + self.assertEqual(quota_set_check['cores'], 20) + self.assertEqual(quota_set_check['ram'], 51200) + self.assertEqual(quota_set_check['volumes'], 10) + self.assertEqual(quota_set_check['gigabytes'], 1000) + self.assertEqual(quota_set_check['floating_ips'], 10) + self.assertEqual(quota_set_check['metadata_items'], 128) + self.assertEqual(quota_set_check['injected_files'], 5) + self.assertEqual(quota_set_check['injected_file_content_bytes'], 10240) + + def test_quotas_index_with_default_param(self): + req = webob.Request.blank('/v1.1/os-quotas?defaults=True') + req.method = 'GET' + req.headers['Content-Type'] = 'application/json' + res = req.get_response(fakes.wsgi_app()) + + self.assertEqual(res.status_int, 200) + expected = {'quota_set_list': [{'quota_set': { + 'id': '__defaults__', + 'instances': 10, + 'cores': 20, + 'ram': 51200, + 'volumes': 10, + 'gigabytes': 1000, + 'floating_ips': 10, + 'metadata_items': 128, + 'injected_files': 5, + 'injected_file_content_bytes': 10240}}]} + + self.assertEqual(json.loads(res.body), expected) + + def test_quotas_index(self): + req = webob.Request.blank('/v1.1/os-quotas') + req.method = 'GET' + req.headers['Content-Type'] = 'application/json' + res = req.get_response(fakes.wsgi_app()) + + self.assertEqual(res.status_int, 200) + self.assertEqual(json.loads(res.body), quota_set_list()) + + def test_quotas_show(self): + req = webob.Request.blank('/v1.1/os-quotas/1234') + req.method = 'GET' + req.headers['Content-Type'] = 'application/json' + res = req.get_response(fakes.wsgi_app()) + + self.assertEqual(res.status_int, 200) + self.assertEqual(json.loads(res.body), quota_set('1234')) + + def test_quotas_update(self): + updated_quota_set = {'quota_set': {'instances': 50, + 'cores': 50, 'ram': 51200, 'volumes': 10, + 'gigabytes': 1000, 'floating_ips': 10, + 'metadata_items': 128, 'injected_files': 5, + 'injected_file_content_bytes': 10240}} + + req = webob.Request.blank('/v1.1/os-quotas/update_me') + req.method = 'PUT' + req.body = json.dumps(updated_quota_set) + req.headers['Content-Type'] = 'application/json' + + res = req.get_response(fakes.wsgi_app(fake_auth_context=\ + context.RequestContext('fake', 'fake', + is_admin=True))) + + self.assertEqual(json.loads(res.body), updated_quota_set) diff --git a/nova/tests/api/openstack/test_extensions.py b/nova/tests/api/openstack/test_extensions.py index 3e990a30b..0ae1a059f 100644 --- a/nova/tests/api/openstack/test_extensions.py +++ b/nova/tests/api/openstack/test_extensions.py @@ -91,6 +91,7 @@ class ExtensionControllerTest(test.TestCase): "Hosts", "Keypairs", "Multinic", + "Quotas", "SecurityGroups", "Volumes", ] -- cgit From b6c8985cb10b40572d23b7971aac6d0577ebfe82 Mon Sep 17 00:00:00 2001 From: Jake Dahn Date: Tue, 16 Aug 2011 09:15:01 -0700 Subject: another trunk merge --- nova/tests/api/openstack/contrib/test_quotas.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/tests/api/openstack/contrib/test_quotas.py b/nova/tests/api/openstack/contrib/test_quotas.py index 6ab2faf4d..6a7a1d9fa 100644 --- a/nova/tests/api/openstack/contrib/test_quotas.py +++ b/nova/tests/api/openstack/contrib/test_quotas.py @@ -151,7 +151,7 @@ class QuotasTest(test.TestCase): req.body = json.dumps(updated_quota_set) req.headers['Content-Type'] = 'application/json' - res = req.get_response(fakes.wsgi_app(fake_auth_context=\ + res = req.get_response(fakes.wsgi_app(fake_auth_context= context.RequestContext('fake', 'fake', is_admin=True))) -- cgit From e9cf4ff5c14b274b8a1d7aa39567768368851e81 Mon Sep 17 00:00:00 2001 From: Jake Dahn Date: Tue, 16 Aug 2011 09:15:14 -0700 Subject: merging test_extensions.py --- nova/tests/api/openstack/test_extensions.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/nova/tests/api/openstack/test_extensions.py b/nova/tests/api/openstack/test_extensions.py index 0ae1a059f..5d3208e10 100644 --- a/nova/tests/api/openstack/test_extensions.py +++ b/nova/tests/api/openstack/test_extensions.py @@ -111,7 +111,7 @@ class ExtensionControllerTest(test.TestCase): self.assertEqual(names, self.ext_list) # Make sure that at least Fox in Sox is correct. - (fox_ext,) = [ + (fox_ext, ) = [ x for x in data['extensions'] if x['alias'] == 'FOXNSOX'] self.assertEqual(fox_ext, { 'namespace': 'http://www.fox.in.socks/api/ext/pie/v1.0', @@ -156,7 +156,7 @@ class ExtensionControllerTest(test.TestCase): self.assertEqual(len(exts), len(self.ext_list)) # Make sure that at least Fox in Sox is correct. - (fox_ext,) = [x for x in exts if x.get('alias') == 'FOXNSOX'] + (fox_ext, ) = [x for x in exts if x.get('alias') == 'FOXNSOX'] self.assertEqual(fox_ext.get('name'), 'Fox In Socks') self.assertEqual(fox_ext.get('namespace'), 'http://www.fox.in.socks/api/ext/pie/v1.0') @@ -228,6 +228,7 @@ class ResourceExtensionTest(test.TestCase): class InvalidExtension(object): + def get_alias(self): return "THIRD" -- cgit From 02c5d589483abef3fb8ec65f983e5b43a9e41f71 Mon Sep 17 00:00:00 2001 From: Jake Dahn Date: Tue, 16 Aug 2011 09:15:14 -0700 Subject: removed index, and separated out defaults into its own action --- nova/api/openstack/contrib/quotas.py | 28 ++++++--------------- nova/tests/api/openstack/contrib/test_quotas.py | 33 +++++++++---------------- 2 files changed, 20 insertions(+), 41 deletions(-) diff --git a/nova/api/openstack/contrib/quotas.py b/nova/api/openstack/contrib/quotas.py index 7dbafb79a..5f2b54d57 100644 --- a/nova/api/openstack/contrib/quotas.py +++ b/nova/api/openstack/contrib/quotas.py @@ -24,7 +24,7 @@ from nova.auth import manager as auth_manager from nova.api.openstack import extensions -class QuotasController(object): +class QuotaSetsController(object): def _format_quota_set(self, project_id, quota_set): """Convert the quota object to a result dict""" @@ -43,22 +43,6 @@ class QuotasController(object): 'cores': quota_set['cores'], }} - def index(self, req): - # NOTE(jakedahn): If http param defaults is true, list system defaults. - if urlparse.parse_qs(req.environ['QUERY_STRING']).get('defaults', - False): - return {'quota_set_list': [self._format_quota_set('__defaults__', - quota._get_default_quotas())]} - else: - context = req.environ['nova.context'] - user = req.environ.get('user') - projects = auth_manager.AuthManager().get_projects(user=user) - - quota_set_list = [self._format_quota_set(project.name, - quota.get_project_quotas(context, project.name)) - for project in projects] - return {'quota_set_list': quota_set_list} - def show(self, req, id): context = req.environ['nova.context'] return self._format_quota_set(id, quota.get_project_quotas(context, @@ -80,6 +64,8 @@ class QuotasController(object): db.quota_create(context, project_id, key, value) return {'quota_set': quota.get_project_quotas(context, project_id)} + def defaults(self, req): + return self._format_quota_set('defaults', quota._get_default_quotas()) class Quotas(extensions.ExtensionDescriptor): @@ -87,13 +73,13 @@ class Quotas(extensions.ExtensionDescriptor): return "Quotas" def get_alias(self): - return "os-quotas" + return "os-quota-sets" def get_description(self): return "Quotas management support" def get_namespace(self): - return "http://docs.openstack.org/ext/quotas/api/v1.1" + return "http://docs.openstack.org/ext/quotas-sets/api/v1.1" def get_updated(self): return "2011-08-08T00:00:00+00:00" @@ -101,7 +87,9 @@ class Quotas(extensions.ExtensionDescriptor): def get_resources(self): resources = [] - res = extensions.ResourceExtension('os-quotas', QuotasController()) + res = extensions.ResourceExtension('os-quota-sets', + QuotaSetsController(), + member_actions={'defaults': 'GET'}) resources.append(res) return resources diff --git a/nova/tests/api/openstack/contrib/test_quotas.py b/nova/tests/api/openstack/contrib/test_quotas.py index 6a7a1d9fa..decc76b4e 100644 --- a/nova/tests/api/openstack/contrib/test_quotas.py +++ b/nova/tests/api/openstack/contrib/test_quotas.py @@ -24,7 +24,7 @@ from nova.auth import manager as auth_manager from nova.tests.api.openstack import fakes -from nova.api.openstack.contrib.quotas import QuotasController +from nova.api.openstack.contrib.quotas import QuotaSetsController def quota_set(id): @@ -55,11 +55,11 @@ def delete_user(name): auth_manager.AuthManager().delete_user(name) -class QuotasTest(test.TestCase): +class QuotaSetsTest(test.TestCase): def setUp(self): - super(QuotasTest, self).setUp() - self.controller = QuotasController() + super(QuotaSetsTest, self).setUp() + self.controller = QuotaSetsController() self.context = context.get_admin_context() create_admin_user('foo') @@ -86,7 +86,7 @@ class QuotasTest(test.TestCase): 'injected_file_content_bytes': 10240, } - quota_set = QuotasController()._format_quota_set('1234', raw_quota_set) + quota_set = QuotaSetsController()._format_quota_set('1234', raw_quota_set) quota_set_check = quota_set['quota_set'] self.assertEqual(quota_set_check['id'], '1234') @@ -100,15 +100,15 @@ class QuotasTest(test.TestCase): self.assertEqual(quota_set_check['injected_files'], 5) self.assertEqual(quota_set_check['injected_file_content_bytes'], 10240) - def test_quotas_index_with_default_param(self): - req = webob.Request.blank('/v1.1/os-quotas?defaults=True') + def test_quotas_defaults(self): + req = webob.Request.blank('/v1.1/os-quota-sets/defaults') req.method = 'GET' req.headers['Content-Type'] = 'application/json' res = req.get_response(fakes.wsgi_app()) self.assertEqual(res.status_int, 200) - expected = {'quota_set_list': [{'quota_set': { - 'id': '__defaults__', + expected = {'quota_set': { + 'id': 'defaults', 'instances': 10, 'cores': 20, 'ram': 51200, @@ -117,21 +117,12 @@ class QuotasTest(test.TestCase): 'floating_ips': 10, 'metadata_items': 128, 'injected_files': 5, - 'injected_file_content_bytes': 10240}}]} + 'injected_file_content_bytes': 10240}} self.assertEqual(json.loads(res.body), expected) - def test_quotas_index(self): - req = webob.Request.blank('/v1.1/os-quotas') - req.method = 'GET' - req.headers['Content-Type'] = 'application/json' - res = req.get_response(fakes.wsgi_app()) - - self.assertEqual(res.status_int, 200) - self.assertEqual(json.loads(res.body), quota_set_list()) - def test_quotas_show(self): - req = webob.Request.blank('/v1.1/os-quotas/1234') + req = webob.Request.blank('/v1.1/os-quota-sets/1234') req.method = 'GET' req.headers['Content-Type'] = 'application/json' res = req.get_response(fakes.wsgi_app()) @@ -146,7 +137,7 @@ class QuotasTest(test.TestCase): 'metadata_items': 128, 'injected_files': 5, 'injected_file_content_bytes': 10240}} - req = webob.Request.blank('/v1.1/os-quotas/update_me') + req = webob.Request.blank('/v1.1/os-quota-sets/update_me') req.method = 'PUT' req.body = json.dumps(updated_quota_set) req.headers['Content-Type'] = 'application/json' -- cgit From 817b596bccd38f84f72e1ee73df3c3b35287c75c Mon Sep 17 00:00:00 2001 From: Jake Dahn Date: Tue, 16 Aug 2011 09:15:14 -0700 Subject: refactoring tests to not use authmanager, and now returning 403 when non admin user tries to update quotas --- nova/api/openstack/contrib/quotas.py | 4 +- nova/tests/api/openstack/contrib/test_quotas.py | 113 ++++++++++++------------ 2 files changed, 58 insertions(+), 59 deletions(-) diff --git a/nova/api/openstack/contrib/quotas.py b/nova/api/openstack/contrib/quotas.py index 5f2b54d57..f7e7b4105 100644 --- a/nova/api/openstack/contrib/quotas.py +++ b/nova/api/openstack/contrib/quotas.py @@ -15,6 +15,7 @@ # License for the specific language governing permissions and limitations # under the License. +import webob import urlparse from nova import db @@ -54,7 +55,6 @@ class QuotaSetsController(object): resources = ['metadata_items', 'injected_file_content_bytes', 'volumes', 'gigabytes', 'ram', 'floating_ips', 'instances', 'injected_files', 'cores'] - for key in body['quota_set'].keys(): if key in resources: value = int(body['quota_set'][key]) @@ -62,6 +62,8 @@ class QuotaSetsController(object): db.quota_update(context, project_id, key, value) except exception.ProjectQuotaNotFound: db.quota_create(context, project_id, key, value) + except exception.AdminRequired as e: + return webob.Response(status_int=403) return {'quota_set': quota.get_project_quotas(context, project_id)} def defaults(self, req): diff --git a/nova/tests/api/openstack/contrib/test_quotas.py b/nova/tests/api/openstack/contrib/test_quotas.py index decc76b4e..e2bd05428 100644 --- a/nova/tests/api/openstack/contrib/test_quotas.py +++ b/nova/tests/api/openstack/contrib/test_quotas.py @@ -39,39 +39,18 @@ def quota_set_list(): quota_set('update_me')]} -def create_project(project_name, manager_user): - auth_manager.AuthManager().create_project(project_name, manager_user) - - -def delete_project(project_name): - auth_manager.AuthManager().delete_project(project_name) - - -def create_admin_user(name): - auth_manager.AuthManager().create_user(name, admin=True) - - -def delete_user(name): - auth_manager.AuthManager().delete_user(name) - - class QuotaSetsTest(test.TestCase): def setUp(self): super(QuotaSetsTest, self).setUp() self.controller = QuotaSetsController() - self.context = context.get_admin_context() - - create_admin_user('foo') - create_project('1234', 'foo') - create_project('5678', 'foo') - create_project('update_me', 'foo') - - def tearDown(self): - delete_project('1234') - delete_project('5678') - delete_project('update_me') - delete_user('foo') + self.user_id = 'fake' + self.project_id = 'fake' + self.user_context = context.RequestContext(self.user_id, + self.project_id) + self.admin_context = context.RequestContext(self.user_id, + self.project_id, + is_admin=True) def test_format_quota_set(self): raw_quota_set = { @@ -83,22 +62,22 @@ class QuotaSetsTest(test.TestCase): 'floating_ips': 10, 'metadata_items': 128, 'injected_files': 5, - 'injected_file_content_bytes': 10240, - } - - quota_set = QuotaSetsController()._format_quota_set('1234', raw_quota_set) - quota_set_check = quota_set['quota_set'] - - self.assertEqual(quota_set_check['id'], '1234') - self.assertEqual(quota_set_check['instances'], 10) - self.assertEqual(quota_set_check['cores'], 20) - self.assertEqual(quota_set_check['ram'], 51200) - self.assertEqual(quota_set_check['volumes'], 10) - self.assertEqual(quota_set_check['gigabytes'], 1000) - self.assertEqual(quota_set_check['floating_ips'], 10) - self.assertEqual(quota_set_check['metadata_items'], 128) - self.assertEqual(quota_set_check['injected_files'], 5) - self.assertEqual(quota_set_check['injected_file_content_bytes'], 10240) + 'injected_file_content_bytes': 10240} + + quota_set = QuotaSetsController()._format_quota_set('1234', + raw_quota_set) + qs = quota_set['quota_set'] + + self.assertEqual(qs['id'], '1234') + self.assertEqual(qs['instances'], 10) + self.assertEqual(qs['cores'], 20) + self.assertEqual(qs['ram'], 51200) + self.assertEqual(qs['volumes'], 10) + self.assertEqual(qs['gigabytes'], 1000) + self.assertEqual(qs['floating_ips'], 10) + self.assertEqual(qs['metadata_items'], 128) + self.assertEqual(qs['injected_files'], 5) + self.assertEqual(qs['injected_file_content_bytes'], 10240) def test_quotas_defaults(self): req = webob.Request.blank('/v1.1/os-quota-sets/defaults') @@ -108,16 +87,16 @@ class QuotaSetsTest(test.TestCase): self.assertEqual(res.status_int, 200) expected = {'quota_set': { - 'id': 'defaults', - 'instances': 10, - 'cores': 20, - 'ram': 51200, - 'volumes': 10, - 'gigabytes': 1000, - 'floating_ips': 10, - 'metadata_items': 128, - 'injected_files': 5, - 'injected_file_content_bytes': 10240}} + 'id': 'defaults', + 'instances': 10, + 'cores': 20, + 'ram': 51200, + 'volumes': 10, + 'gigabytes': 1000, + 'floating_ips': 10, + 'metadata_items': 128, + 'injected_files': 5, + 'injected_file_content_bytes': 10240}} self.assertEqual(json.loads(res.body), expected) @@ -125,12 +104,13 @@ class QuotaSetsTest(test.TestCase): req = webob.Request.blank('/v1.1/os-quota-sets/1234') req.method = 'GET' req.headers['Content-Type'] = 'application/json' - res = req.get_response(fakes.wsgi_app()) + res = req.get_response(fakes.wsgi_app(fake_auth_context= + self.admin_context)) self.assertEqual(res.status_int, 200) self.assertEqual(json.loads(res.body), quota_set('1234')) - def test_quotas_update(self): + def test_quotas_update_as_admin(self): updated_quota_set = {'quota_set': {'instances': 50, 'cores': 50, 'ram': 51200, 'volumes': 10, 'gigabytes': 1000, 'floating_ips': 10, @@ -143,7 +123,24 @@ class QuotaSetsTest(test.TestCase): req.headers['Content-Type'] = 'application/json' res = req.get_response(fakes.wsgi_app(fake_auth_context= - context.RequestContext('fake', 'fake', - is_admin=True))) + self.admin_context)) self.assertEqual(json.loads(res.body), updated_quota_set) + + + def test_quotas_update_as_user(self): + updated_quota_set = {'quota_set': {'instances': 50, + 'cores': 50, 'ram': 51200, 'volumes': 10, + 'gigabytes': 1000, 'floating_ips': 10, + 'metadata_items': 128, 'injected_files': 5, + 'injected_file_content_bytes': 10240}} + + req = webob.Request.blank('/v1.1/os-quota-sets/update_me') + req.method = 'PUT' + req.body = json.dumps(updated_quota_set) + req.headers['Content-Type'] = 'application/json' + + res = req.get_response(fakes.wsgi_app(fake_auth_context= + self.user_context)) + + self.assertEqual(res.status_int, 403) -- cgit From 903ae5a8274051aaf40a62c929117d7165729360 Mon Sep 17 00:00:00 2001 From: Jake Dahn Date: Tue, 16 Aug 2011 09:15:14 -0700 Subject: fixing pep8 errors --- nova/api/openstack/contrib/quotas.py | 2 +- nova/tests/api/openstack/contrib/test_quotas.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/nova/api/openstack/contrib/quotas.py b/nova/api/openstack/contrib/quotas.py index f7e7b4105..b5c6447c4 100644 --- a/nova/api/openstack/contrib/quotas.py +++ b/nova/api/openstack/contrib/quotas.py @@ -21,7 +21,6 @@ import urlparse from nova import db from nova import exception from nova import quota -from nova.auth import manager as auth_manager from nova.api.openstack import extensions @@ -69,6 +68,7 @@ class QuotaSetsController(object): def defaults(self, req): return self._format_quota_set('defaults', quota._get_default_quotas()) + class Quotas(extensions.ExtensionDescriptor): def get_name(self): diff --git a/nova/tests/api/openstack/contrib/test_quotas.py b/nova/tests/api/openstack/contrib/test_quotas.py index e2bd05428..b37edb9f5 100644 --- a/nova/tests/api/openstack/contrib/test_quotas.py +++ b/nova/tests/api/openstack/contrib/test_quotas.py @@ -127,7 +127,6 @@ class QuotaSetsTest(test.TestCase): self.assertEqual(json.loads(res.body), updated_quota_set) - def test_quotas_update_as_user(self): updated_quota_set = {'quota_set': {'instances': 50, 'cores': 50, 'ram': 51200, 'volumes': 10, -- cgit From 8b6e551813ef964af38335fcca749ab9d0971200 Mon Sep 17 00:00:00 2001 From: Jake Dahn Date: Tue, 16 Aug 2011 09:15:14 -0700 Subject: cleaning up a few things from pyflakes --- nova/api/openstack/contrib/quotas.py | 3 +-- nova/tests/api/openstack/contrib/test_quotas.py | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/nova/api/openstack/contrib/quotas.py b/nova/api/openstack/contrib/quotas.py index b5c6447c4..d021a4a4f 100644 --- a/nova/api/openstack/contrib/quotas.py +++ b/nova/api/openstack/contrib/quotas.py @@ -16,7 +16,6 @@ # under the License. import webob -import urlparse from nova import db from nova import exception @@ -61,7 +60,7 @@ class QuotaSetsController(object): db.quota_update(context, project_id, key, value) except exception.ProjectQuotaNotFound: db.quota_create(context, project_id, key, value) - except exception.AdminRequired as e: + except exception.AdminRequired: return webob.Response(status_int=403) return {'quota_set': quota.get_project_quotas(context, project_id)} diff --git a/nova/tests/api/openstack/contrib/test_quotas.py b/nova/tests/api/openstack/contrib/test_quotas.py index b37edb9f5..cb5fcb120 100644 --- a/nova/tests/api/openstack/contrib/test_quotas.py +++ b/nova/tests/api/openstack/contrib/test_quotas.py @@ -20,7 +20,6 @@ import webob from nova import context from nova import test -from nova.auth import manager as auth_manager from nova.tests.api.openstack import fakes -- cgit From a3a0782f65d85c873c2ec3fc8f94486225cb6f76 Mon Sep 17 00:00:00 2001 From: Jake Dahn Date: Tue, 16 Aug 2011 09:15:14 -0700 Subject: fixing spacing issues --- nova/tests/api/openstack/contrib/test_quotas.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/nova/tests/api/openstack/contrib/test_quotas.py b/nova/tests/api/openstack/contrib/test_quotas.py index cb5fcb120..f40a435aa 100644 --- a/nova/tests/api/openstack/contrib/test_quotas.py +++ b/nova/tests/api/openstack/contrib/test_quotas.py @@ -22,7 +22,6 @@ from nova import context from nova import test from nova.tests.api.openstack import fakes - from nova.api.openstack.contrib.quotas import QuotaSetsController @@ -57,9 +56,9 @@ class QuotaSetsTest(test.TestCase): 'cores': 20, 'ram': 51200, 'volumes': 10, - 'gigabytes': 1000, 'floating_ips': 10, 'metadata_items': 128, + 'gigabytes': 1000, 'injected_files': 5, 'injected_file_content_bytes': 10240} @@ -103,7 +102,7 @@ class QuotaSetsTest(test.TestCase): req = webob.Request.blank('/v1.1/os-quota-sets/1234') req.method = 'GET' req.headers['Content-Type'] = 'application/json' - res = req.get_response(fakes.wsgi_app(fake_auth_context= + res = req.get_response(fakes.wsgi_app(fake_auth_context = self.admin_context)) self.assertEqual(res.status_int, 200) @@ -121,7 +120,7 @@ class QuotaSetsTest(test.TestCase): req.body = json.dumps(updated_quota_set) req.headers['Content-Type'] = 'application/json' - res = req.get_response(fakes.wsgi_app(fake_auth_context= + res = req.get_response(fakes.wsgi_app(fake_auth_context = self.admin_context)) self.assertEqual(json.loads(res.body), updated_quota_set) @@ -138,7 +137,7 @@ class QuotaSetsTest(test.TestCase): req.body = json.dumps(updated_quota_set) req.headers['Content-Type'] = 'application/json' - res = req.get_response(fakes.wsgi_app(fake_auth_context= + res = req.get_response(fakes.wsgi_app(fake_auth_context = self.user_context)) self.assertEqual(res.status_int, 403) -- cgit From bf269b3d799a431ad3fc68cdb039b826685c8760 Mon Sep 17 00:00:00 2001 From: Jake Dahn Date: Tue, 16 Aug 2011 09:15:14 -0700 Subject: fixing pep8 issues again --- nova/tests/api/openstack/contrib/test_quotas.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/nova/tests/api/openstack/contrib/test_quotas.py b/nova/tests/api/openstack/contrib/test_quotas.py index f40a435aa..8f363aed6 100644 --- a/nova/tests/api/openstack/contrib/test_quotas.py +++ b/nova/tests/api/openstack/contrib/test_quotas.py @@ -102,8 +102,8 @@ class QuotaSetsTest(test.TestCase): req = webob.Request.blank('/v1.1/os-quota-sets/1234') req.method = 'GET' req.headers['Content-Type'] = 'application/json' - res = req.get_response(fakes.wsgi_app(fake_auth_context = - self.admin_context)) + res = req.get_response(fakes.wsgi_app( + fake_auth_context=self.admin_context)) self.assertEqual(res.status_int, 200) self.assertEqual(json.loads(res.body), quota_set('1234')) @@ -120,8 +120,8 @@ class QuotaSetsTest(test.TestCase): req.body = json.dumps(updated_quota_set) req.headers['Content-Type'] = 'application/json' - res = req.get_response(fakes.wsgi_app(fake_auth_context = - self.admin_context)) + res = req.get_response(fakes.wsgi_app( + fake_auth_context=self.admin_context)) self.assertEqual(json.loads(res.body), updated_quota_set) @@ -137,7 +137,7 @@ class QuotaSetsTest(test.TestCase): req.body = json.dumps(updated_quota_set) req.headers['Content-Type'] = 'application/json' - res = req.get_response(fakes.wsgi_app(fake_auth_context = - self.user_context)) + res = req.get_response(fakes.wsgi_app( + fake_auth_context=self.user_context)) self.assertEqual(res.status_int, 403) -- cgit From 029261908ac5acd9950a4b027b7daec17c92854d Mon Sep 17 00:00:00 2001 From: Jake Dahn Date: Tue, 16 Aug 2011 09:15:14 -0700 Subject: making get project quotas require context which has access to the project/tenant) --- nova/db/sqlalchemy/api.py | 1 + 1 file changed, 1 insertion(+) diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 57a4370d8..184ad60d5 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -1959,6 +1959,7 @@ def quota_get(context, project_id, resource, session=None): @require_context def quota_get_all_by_project(context, project_id): + authorize_project_context(context, project_id) session = get_session() result = {'project_id': project_id} rows = session.query(models.Quota).\ -- cgit From 6a5b9831c4b32053996a99307b7303ca851bf508 Mon Sep 17 00:00:00 2001 From: Jake Dahn Date: Tue, 16 Aug 2011 09:15:14 -0700 Subject: fixing up the show quotas tests, and extension --- nova/api/openstack/contrib/quotas.py | 8 ++++++-- nova/tests/api/openstack/contrib/test_quotas.py | 16 +++++++++++++--- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/nova/api/openstack/contrib/quotas.py b/nova/api/openstack/contrib/quotas.py index d021a4a4f..87046063a 100644 --- a/nova/api/openstack/contrib/quotas.py +++ b/nova/api/openstack/contrib/quotas.py @@ -44,8 +44,12 @@ class QuotaSetsController(object): def show(self, req, id): context = req.environ['nova.context'] - return self._format_quota_set(id, quota.get_project_quotas(context, - id)) + try: + db.sqlalchemy.api.authorize_project_context(context, id) + return self._format_quota_set(id, + quota.get_project_quotas(context, id)) + except exception.NotAuthorized: + return webob.Response(status_int=403) def update(self, req, id, body): context = req.environ['nova.context'] diff --git a/nova/tests/api/openstack/contrib/test_quotas.py b/nova/tests/api/openstack/contrib/test_quotas.py index 8f363aed6..d77ed40ed 100644 --- a/nova/tests/api/openstack/contrib/test_quotas.py +++ b/nova/tests/api/openstack/contrib/test_quotas.py @@ -78,14 +78,14 @@ class QuotaSetsTest(test.TestCase): self.assertEqual(qs['injected_file_content_bytes'], 10240) def test_quotas_defaults(self): - req = webob.Request.blank('/v1.1/os-quota-sets/defaults') + req = webob.Request.blank('/v1.1/os-quota-sets/fake_tenant/defaults') req.method = 'GET' req.headers['Content-Type'] = 'application/json' res = req.get_response(fakes.wsgi_app()) self.assertEqual(res.status_int, 200) expected = {'quota_set': { - 'id': 'defaults', + 'id': 'fake_tenant', 'instances': 10, 'cores': 20, 'ram': 51200, @@ -98,7 +98,7 @@ class QuotaSetsTest(test.TestCase): self.assertEqual(json.loads(res.body), expected) - def test_quotas_show(self): + def test_quotas_show_as_admin(self): req = webob.Request.blank('/v1.1/os-quota-sets/1234') req.method = 'GET' req.headers['Content-Type'] = 'application/json' @@ -108,6 +108,16 @@ class QuotaSetsTest(test.TestCase): self.assertEqual(res.status_int, 200) self.assertEqual(json.loads(res.body), quota_set('1234')) + + def test_quotas_show_as_unauthorized_user(self): + req = webob.Request.blank('/v1.1/os-quota-sets/1234') + req.method = 'GET' + req.headers['Content-Type'] = 'application/json' + res = req.get_response(fakes.wsgi_app( + fake_auth_context=self.user_context)) + + self.assertEqual(res.status_int, 403) + def test_quotas_update_as_admin(self): updated_quota_set = {'quota_set': {'instances': 50, 'cores': 50, 'ram': 51200, 'volumes': 10, -- cgit From b85deda977ff46722a4461aca98f0378fd10ee1b Mon Sep 17 00:00:00 2001 From: Jake Dahn Date: Tue, 16 Aug 2011 09:15:14 -0700 Subject: defaults now is referred to using a tenant --- nova/api/openstack/contrib/quotas.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nova/api/openstack/contrib/quotas.py b/nova/api/openstack/contrib/quotas.py index 87046063a..459b71dfd 100644 --- a/nova/api/openstack/contrib/quotas.py +++ b/nova/api/openstack/contrib/quotas.py @@ -68,8 +68,8 @@ class QuotaSetsController(object): return webob.Response(status_int=403) return {'quota_set': quota.get_project_quotas(context, project_id)} - def defaults(self, req): - return self._format_quota_set('defaults', quota._get_default_quotas()) + def defaults(self, req, id): + return self._format_quota_set(id, quota._get_default_quotas()) class Quotas(extensions.ExtensionDescriptor): -- cgit From f3e64fea374df91a6ff78a891ff627edf635fdb2 Mon Sep 17 00:00:00 2001 From: Jake Dahn Date: Tue, 16 Aug 2011 09:15:14 -0700 Subject: fixing pep8 issue --- nova/tests/api/openstack/contrib/test_quotas.py | 1 - 1 file changed, 1 deletion(-) diff --git a/nova/tests/api/openstack/contrib/test_quotas.py b/nova/tests/api/openstack/contrib/test_quotas.py index d77ed40ed..f6a25385f 100644 --- a/nova/tests/api/openstack/contrib/test_quotas.py +++ b/nova/tests/api/openstack/contrib/test_quotas.py @@ -108,7 +108,6 @@ class QuotaSetsTest(test.TestCase): self.assertEqual(res.status_int, 200) self.assertEqual(json.loads(res.body), quota_set('1234')) - def test_quotas_show_as_unauthorized_user(self): req = webob.Request.blank('/v1.1/os-quota-sets/1234') req.method = 'GET' -- cgit From 71935201aed268e94ee9674e887d67b4b9f217a6 Mon Sep 17 00:00:00 2001 From: Brian Lamar Date: Tue, 16 Aug 2011 13:44:03 -0400 Subject: Updated ViewBuilderV10 as per feedback. --- nova/api/openstack/views/servers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nova/api/openstack/views/servers.py b/nova/api/openstack/views/servers.py index 60fdf54be..edc328129 100644 --- a/nova/api/openstack/views/servers.py +++ b/nova/api/openstack/views/servers.py @@ -111,14 +111,14 @@ class ViewBuilderV10(ViewBuilder): response['uuid'] = inst['uuid'] def _build_image(self, response, inst): - if 'image_ref' in dict(inst): + if inst.get('image_ref', None): image_ref = inst['image_ref'] if str(image_ref).startswith('http'): raise exception.ListingImageRefsNotSupported() response['imageId'] = int(image_ref) def _build_flavor(self, response, inst): - if 'instance_type' in dict(inst): + if inst.get('instance_type', None): response['flavorId'] = inst['instance_type']['flavorid'] def _build_addresses(self, response, inst): -- cgit From 935c43b414c1685163957590a6fb77fd8ddbac2f Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Tue, 16 Aug 2011 13:36:11 -0500 Subject: Allow local_gb to be 0; PEP8 fixes. --- nova/compute/manager.py | 10 +++++++++- nova/virt/libvirt/connection.py | 5 +++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 3299268f2..39f43a268 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -359,6 +359,13 @@ class ComputeManager(manager.SchedulerDependentManager): instance_type = self.db.instance_type_get(context, instance_type_id) allowed_size_gb = instance_type['local_gb'] + + if allowed_size_gb == 0: + # NOTE(jk0): Since the default local_gb of m1.tiny is 0, we will + # allow the check to proceed. We may want to look into changing the + # default size to 1GB. + return + allowed_size_bytes = allowed_size_gb * 1024 * 1024 * 1024 LOG.debug(_("image_id=%(image_id)d, image_size_bytes=" @@ -1368,7 +1375,8 @@ class ComputeManager(manager.SchedulerDependentManager): # This nwfilter is necessary on the destination host. # In addition, this method is creating filtering rule # onto destination host. - self.driver.ensure_filtering_rules_for_instance(instance_ref, network_info) + self.driver.ensure_filtering_rules_for_instance(instance_ref, + network_info) # Preparation for block migration if block_migration: diff --git a/nova/virt/libvirt/connection.py b/nova/virt/libvirt/connection.py index 2b17e244a..e8a657bac 100644 --- a/nova/virt/libvirt/connection.py +++ b/nova/virt/libvirt/connection.py @@ -1538,8 +1538,9 @@ class LibvirtConnection(driver.ComputeDriver): # If any instances never launch at destination host, # basic-filtering must be set here. self.firewall_driver.setup_basic_filtering(instance_ref, network_info) - # setting up n)ova-instance-instance-xx mainly. - self.firewall_driver.prepare_instance_filter(instance_ref, network_info) + # setting up nova-instance-instance-xx mainly. + self.firewall_driver.prepare_instance_filter(instance_ref, + network_info) # wait for completion timeout_count = range(FLAGS.live_migration_retry_count) -- cgit From 4ee7e94ab89189c284348c8756da611192dfe5ec Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Tue, 16 Aug 2011 13:43:33 -0500 Subject: Updated note. --- nova/compute/manager.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 39f43a268..88d290908 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -361,9 +361,9 @@ class ComputeManager(manager.SchedulerDependentManager): allowed_size_gb = instance_type['local_gb'] if allowed_size_gb == 0: - # NOTE(jk0): Since the default local_gb of m1.tiny is 0, we will - # allow the check to proceed. We may want to look into changing the - # default size to 1GB. + # NOTE(jk0): Since libvirt uses local_gb as a secondary drive, we + # need to handle potential situations where local_gb is 0. This is + # the default for m1.tiny. return allowed_size_bytes = allowed_size_gb * 1024 * 1024 * 1024 -- cgit From dc2ccb95848c330eeb8e6fa55bf487c54e03a3c3 Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Tue, 16 Aug 2011 13:45:13 -0500 Subject: Review feedback. --- nova/compute/manager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/compute/manager.py b/nova/compute/manager.py index 88d290908..66458fb36 100644 --- a/nova/compute/manager.py +++ b/nova/compute/manager.py @@ -360,10 +360,10 @@ class ComputeManager(manager.SchedulerDependentManager): instance_type_id) allowed_size_gb = instance_type['local_gb'] - if allowed_size_gb == 0: # NOTE(jk0): Since libvirt uses local_gb as a secondary drive, we # need to handle potential situations where local_gb is 0. This is # the default for m1.tiny. + if allowed_size_gb == 0: return allowed_size_bytes = allowed_size_gb * 1024 * 1024 * 1024 -- cgit From ecc4e9ee389115e3793f94aaf53f8fbe59e7ac66 Mon Sep 17 00:00:00 2001 From: Ed Leafe Date: Wed, 17 Aug 2011 19:58:22 +0000 Subject: Added the host 'enabled' status to the host_data returned by the plugin. --- plugins/xenserver/xenapi/etc/xapi.d/plugins/xenhost | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/xenserver/xenapi/etc/xapi.d/plugins/xenhost b/plugins/xenserver/xenapi/etc/xapi.d/plugins/xenhost index cd9694ce1..36c61f78d 100755 --- a/plugins/xenserver/xenapi/etc/xapi.d/plugins/xenhost +++ b/plugins/xenserver/xenapi/etc/xapi.d/plugins/xenhost @@ -258,6 +258,7 @@ def cleanup(dct): # out["host_suspend-image-sr-uuid"] = dct.get("suspend-image-sr-uuid", "") # out["host_crash-dump-sr-uuid"] = dct.get("crash-dump-sr-uuid", "") # out["host_local-cache-sr"] = dct.get("local-cache-sr", "") + out["enabled"] = dct.get("enabled", "true") == "true" out["host_memory"] = omm = {} omm["total"] = safe_int(dct.get("memory-total", "")) omm["overhead"] = safe_int(dct.get("memory-overhead", "")) -- cgit