From 3dbb3fa96779fbed89251a7c24455acbddc013e5 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Sat, 11 Sep 2010 20:05:32 -0700 Subject: fixed reversed args in nova-manage project environment --- bin/nova-manage | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/nova-manage b/bin/nova-manage index 325245ac4..1c6dc03ac 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -252,7 +252,7 @@ class ProjectCommands(object): def environment(self, project_id, user_id, filename='novarc'): """Exports environment variables to an sourcable file arguments: project_id user_id [filename='novarc]""" - rc = self.manager.get_environment_rc(project_id, user_id) + rc = self.manager.get_environment_rc(user_id, project_id) with open(filename, 'w') as f: f.write(rc) -- cgit From 970654239267fc702f767bbaff3e22207576d0cd Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Mon, 13 Sep 2010 01:58:40 -0700 Subject: multiple network controllers will not create duplicate indexes --- nova/db/api.py | 9 ++++++--- nova/db/sqlalchemy/api.py | 8 ++++++-- nova/db/sqlalchemy/models.py | 2 +- nova/network/manager.py | 2 +- 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/nova/db/api.py b/nova/db/api.py index 9f6ff99c3..a775dc301 100644 --- a/nova/db/api.py +++ b/nova/db/api.py @@ -365,9 +365,12 @@ def network_index_count(context): return IMPL.network_index_count(context) -def network_index_create(context, values): - """Create a network index from the values dict""" - return IMPL.network_index_create(context, values) +def network_index_create_safe(context, values): + """Create a network index from the values dict + + The index is not returned. If the create violates the unique + constraints because the index already exists, no exception is raised.""" + return IMPL.network_index_create_safe(context, values) def network_set_cidr(context, network_id, cidr): diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index d612fe669..35585b1b0 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -25,6 +25,7 @@ from nova import flags from nova.db.sqlalchemy import models from nova.db.sqlalchemy.session import get_session from sqlalchemy import or_ +from sqlalchemy.exc import IntegrityError from sqlalchemy.orm import joinedload_all from sqlalchemy.sql import func @@ -567,11 +568,14 @@ def network_index_count(_context): return models.NetworkIndex.count() -def network_index_create(_context, values): +def network_index_create_safe(_context, values): network_index_ref = models.NetworkIndex() for (key, value) in values.iteritems(): network_index_ref[key] = value - network_index_ref.save() + try: + network_index_ref.save() + except IntegrityError: + pass def network_set_host(_context, network_id, host_id): diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index 41013f41b..6ff0decb5 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -355,7 +355,7 @@ class NetworkIndex(BASE, NovaBase): """ __tablename__ = 'network_indexes' id = Column(Integer, primary_key=True) - index = Column(Integer) + index = Column(Integer, unique=True) network_id = Column(Integer, ForeignKey('networks.id'), nullable=True) network = relationship(Network, backref=backref('network_index', uselist=False)) diff --git a/nova/network/manager.py b/nova/network/manager.py index 191c1d364..570d5f8d8 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -343,7 +343,7 @@ class VlanManager(NetworkManager): This could use a manage command instead of keying off of a flag""" if not self.db.network_index_count(context): for index in range(FLAGS.num_networks): - self.db.network_index_create(context, {'index': index}) + self.db.network_index_create_safe(context, {'index': index}) def _on_set_network_host(self, context, network_id): """Called when this host becomes the host for a project""" -- cgit From 01a757ee7bc3624c17dbbcfd3bc65d3e2f674b03 Mon Sep 17 00:00:00 2001 From: Devin Carlen Date: Wed, 15 Sep 2010 17:40:12 -0700 Subject: Added iptables host initial configuration --- bin/nova-manage | 1 + nova/endpoint/api.py | 3 +- nova/flags.py | 5 ++- nova/manager.py | 10 +++++ nova/network/linux_net.py | 44 +++++++++++++++------- nova/network/manager.py | 7 ++++ nova/service.py | 1 + tools/setup_ipchains.sh | 94 +++++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 148 insertions(+), 17 deletions(-) create mode 100644 tools/setup_ipchains.sh diff --git a/bin/nova-manage b/bin/nova-manage index 325245ac4..909435ede 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -384,3 +384,4 @@ def main(): if __name__ == '__main__': main() + diff --git a/nova/endpoint/api.py b/nova/endpoint/api.py index 40be00bb7..6de3698e1 100755 --- a/nova/endpoint/api.py +++ b/nova/endpoint/api.py @@ -42,8 +42,6 @@ from nova.endpoint import cloud FLAGS = flags.FLAGS -flags.DEFINE_integer('cc_port', 8773, 'cloud controller port') - _log = logging.getLogger("api") _log.setLevel(logging.DEBUG) @@ -342,3 +340,4 @@ class APIServerApplication(tornado.web.Application): (r'/1.0/([-A-Za-z0-9/]*)', MetadataRequestHandler), ], pool=multiprocessing.Pool(4)) self.controllers = controllers + diff --git a/nova/flags.py b/nova/flags.py index 7b0c95a3c..55b452fc3 100644 --- a/nova/flags.py +++ b/nova/flags.py @@ -184,7 +184,9 @@ DEFINE_string('rabbit_userid', 'guest', 'rabbit userid') DEFINE_string('rabbit_password', 'guest', 'rabbit password') DEFINE_string('rabbit_virtual_host', '/', 'rabbit virtual host') DEFINE_string('control_exchange', 'nova', 'the main exchange to connect to') -DEFINE_string('ec2_url', 'http://127.0.0.1:8773/services/Cloud', +DEFINE_string('cc_ip', '127.0.0.1', 'ip of api server') +DEFINE_integer('cc_port', 8773, 'cloud controller port') +DEFINE_string('ec2_url', 'http://%s:%s/services/Cloud' % (FLAGS.cc_ip, FLAGS.cc_port), 'Url to ec2 api server') DEFINE_string('default_image', 'ami-11111', @@ -220,3 +222,4 @@ DEFINE_string('host', socket.gethostname(), # UNUSED DEFINE_string('node_availability_zone', 'nova', 'availability zone of this node') + diff --git a/nova/manager.py b/nova/manager.py index e9aa50c56..495b1f0d1 100644 --- a/nova/manager.py +++ b/nova/manager.py @@ -37,3 +37,13 @@ class Manager(object): if not db_driver: db_driver = FLAGS.db_driver self.db = utils.import_object(db_driver) # pylint: disable-msg=C0103 + + def init_host(self): + """Do any initialization that needs to be run if this is a standalone service. + + Child classes should override this method. + """ + + + + diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index 41aeb5da7..604d11c93 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -36,13 +36,28 @@ flags.DEFINE_string('dhcpbridge_flagfile', flags.DEFINE_string('networks_path', utils.abspath('../networks'), 'Location to keep network config files') flags.DEFINE_string('public_interface', 'vlan1', - 'Interface for public IP addresses') + 'Interface for public IP addresses') flags.DEFINE_string('bridge_dev', 'eth0', - 'network device for bridges') - + 'network device for bridges') +flags.DEFINE_string('routing_source_ip', utils.get_my_ip(), + 'Public IP of network host') DEFAULT_PORTS = [("tcp", 80), ("tcp", 22), ("udp", 1194), ("tcp", 443)] +def init_host(): + """Basic networking setup goes here""" + # NOTE(devcamcar): Cloud public DNAT entries, CloudPipe port + # forwarding entries and a default DNAT entry. + _confirm_rule("-t nat -A nova_prerouting -s 0.0.0.0/0 " + "-d 169.254.169.254/32 -p tcp -m tcp --dport 80 -j DNAT " + "--to-destination %s:%s" % (FLAGS.cc_ip, FLAGS.cc_port)) + + # NOTE(devcamcar): Cloud public SNAT entries and the default + # SNAT rule for outbound traffic. + _confirm_rule("-t nat -A nova_postrouting -s %s " + "-j SNAT --to-source %s" + % (FLAGS.private_range, FLAGS.routing_source_ip)) + def bind_floating_ip(floating_ip): """Bind ip to public interface""" @@ -58,37 +73,37 @@ def unbind_floating_ip(floating_ip): def ensure_vlan_forward(public_ip, port, private_ip): """Sets up forwarding rules for vlan""" - _confirm_rule("FORWARD -d %s -p udp --dport 1194 -j ACCEPT" % private_ip) + _confirm_rule("nova_forward -d %s -p udp --dport 1194 -j ACCEPT" % private_ip) _confirm_rule( - "PREROUTING -t nat -d %s -p udp --dport %s -j DNAT --to %s:1194" + "nova_prerouting -t nat -d %s -p udp --dport %s -j DNAT --to %s:1194" % (public_ip, port, private_ip)) def ensure_floating_forward(floating_ip, fixed_ip): """Ensure floating ip forwarding rule""" - _confirm_rule("PREROUTING -t nat -d %s -j DNAT --to %s" + _confirm_rule("nova_prerouting -t nat -d %s -j DNAT --to %s" % (floating_ip, fixed_ip)) - _confirm_rule("POSTROUTING -t nat -s %s -j SNAT --to %s" + _confirm_rule("nova_postrouting -t nat -s %s -j SNAT --to %s" % (fixed_ip, floating_ip)) # TODO(joshua): Get these from the secgroup datastore entries - _confirm_rule("FORWARD -d %s -p icmp -j ACCEPT" + _confirm_rule("nova_forward -d %s -p icmp -j ACCEPT" % (fixed_ip)) for (protocol, port) in DEFAULT_PORTS: _confirm_rule( - "FORWARD -d %s -p %s --dport %s -j ACCEPT" + "nova_forward -d %s -p %s --dport %s -j ACCEPT" % (fixed_ip, protocol, port)) def remove_floating_forward(floating_ip, fixed_ip): """Remove forwarding for floating ip""" - _remove_rule("PREROUTING -t nat -d %s -j DNAT --to %s" + _remove_rule("nova_prerouting -t nat -d %s -j DNAT --to %s" % (floating_ip, fixed_ip)) - _remove_rule("POSTROUTING -t nat -s %s -j SNAT --to %s" + _remove_rule("nova_postrouting -t nat -s %s -j SNAT --to %s" % (fixed_ip, floating_ip)) - _remove_rule("FORWARD -d %s -p icmp -j ACCEPT" + _remove_rule("nova_forward -d %s -p icmp -j ACCEPT" % (fixed_ip)) for (protocol, port) in DEFAULT_PORTS: - _remove_rule("FORWARD -d %s -p %s --dport %s -j ACCEPT" + _remove_rule("nova_forward -d %s -p %s --dport %s -j ACCEPT" % (fixed_ip, protocol, port)) @@ -124,7 +139,7 @@ def ensure_bridge(bridge, interface, net_attrs=None): net_attrs['gateway'], net_attrs['broadcast'], net_attrs['netmask'])) - _confirm_rule("FORWARD --in-interface %s -j ACCEPT" % bridge) + _confirm_rule("nova_forward --in-interface %s -j ACCEPT" % bridge) else: _execute("sudo ifconfig %s up" % bridge) @@ -256,3 +271,4 @@ def _dnsmasq_pid_for(vlan): if os.path.exists(pid_file): with open(pid_file, 'r') as f: return int(f.read()) + diff --git a/nova/network/manager.py b/nova/network/manager.py index 7a3bcfc2f..87c3d8e46 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -218,6 +218,12 @@ class FlatManager(NetworkManager): class VlanManager(NetworkManager): """Vlan network with dhcp""" + + def init_host(self): + """Do any initialization that needs to be run if this is a standalone service. + """ + driver.init_host() + def allocate_fixed_ip(self, context, instance_id, *args, **kwargs): """Gets a fixed ip from the pool""" network_ref = self.db.project_get_network(context, context.project.id) @@ -363,3 +369,4 @@ class VlanManager(NetworkManager): parent_reserved = super(VlanManager, self)._top_reserved_ips return parent_reserved + FLAGS.cnt_vpn_clients + diff --git a/nova/service.py b/nova/service.py index 870dd6ceb..8f1db1b8e 100644 --- a/nova/service.py +++ b/nova/service.py @@ -158,3 +158,4 @@ class Service(object, service.Service): self.model_disconnected = True logging.exception("model server went away") yield + diff --git a/tools/setup_ipchains.sh b/tools/setup_ipchains.sh new file mode 100644 index 000000000..b1ab1c6f7 --- /dev/null +++ b/tools/setup_ipchains.sh @@ -0,0 +1,94 @@ +#!/usr/bin/env bash + +CMD="global" +IP="XXX" +PRIVATE_RANGE="10.128.0.0/12" + +if [ -n "$1" ]; then + CMD=$1 +fi + +if [ -n "$2" ]; then + IP=$2 +fi + +if [ -n "$3" ]; then + PRIVATE_RANGE=$3 +fi + +if [ "$CMD" == "global" ]; then + iptables -P INPUT DROP + iptables -A INPUT -m state --state INVALID -j DROP + iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT + iptables -A INPUT -m tcp -p tcp -d $MGMT_IP --dport 22 -j ACCEPT + iptables -A INPUT -m udp -p udp --dport 123 -j ACCEPT + iptables -N nova_input + iptables -A INPUT -j nova_input + iptables -A INPUT -p icmp -j ACCEPT + iptables -A INPUT -p tcp -j REJECT --reject-with tcp-reset + iptables -A INPUT -j REJECT --reject-with icmp-port-unreachable + + iptables -P FORWARD DROP + iptables -A FORWARD -m state --state INVALID -j DROP + iptables -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT + iptables -A FORWARD -p tcp -m tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu + iptables -N nova_forward + iptables -A FORWARD -j nova_forward + + iptables -P OUTPUT DROP + iptables -A OUTPUT -m state --state INVALID -j DROP + iptables -A OUTPUT -m state --state RELATED,ESTABLISHED -j ACCEPT + iptables -N nova_output + iptables -A OUTPUT -j nova_output + + iptables -t nat -N nova_prerouting + iptables -t nat -A PREROUTING -j nova_prerouting + + iptables -t nat -N nova_postrouting + iptables -t nat -A POSTROUTING -j nova_postrouting + + iptables -t nat -N nova_output + iptables -t nat -A OUTPUT -j nova_output + + # ganglia (all hosts) + iptables -A nova_input -m tcp -p tcp -d $IP --dport 8649 -j ACCEPT + iptables -A nova_input -m udp -p udp -d $IP --dport 8649 -j ACCEPT +fi + +if [ "$CMD" == "dashboard" ]; then + # dashboard + iptables -A nova_input -m tcp -p tcp -d $IP --dport 80 -j ACCEPT + iptables -A nova_input -m tcp -p tcp -d $IP --dport 443 -j ACCEPT +fi + +if [ "$CMD" == "objectstore" ]; then + iptables -A nova_input -m tcp -p tcp -d $IP --dport 3333 -j ACCEPT + iptables -A nova_input -m tcp -p tcp -d $IP --dport 8773 -j ACCEPT +fi + +if [ "$CMD" == "redis" ]; then + iptables -A nova_input -m tcp -p tcp -d $IP --dport 6379 -j ACCEPT +fi + +if [ "$CMD" == "mysql" ]; then + iptables -A nova_input -m tcp -p tcp -d $IP --dport 3306 -j ACCEPT +fi + +if [ "$CMD" == "rabbitmq" ]; then + iptables -A nova_input -m tcp -p tcp -d $IP --dport 4369 -j ACCEPT + iptables -A nova_input -m tcp -p tcp -d $IP --dport 5672 -j ACCEPT + iptables -A nova_input -m tcp -p tcp -d $IP --dport 53284 -j ACCEPT +fi + +if [ "$CMD" == "dnsmasq" ]; then + # NOTE(vish): this could theoretically be setup per network + # for each host, but it seems like overkill + iptables -A nova_input -m tcp -p tcp -s $PRIVATE_RANGE --dport 53 -j ACCEPT + iptables -A nova_input -m udp -p udp -s $PRIVATE_RANGE --dport 53 -j ACCEPT + iptables -A nova_input -m udp -p udp --dport 67 -j ACCEPT + +if [ "$CMD" == "ldap" ]; then + iptables -A nova_input -m tcp -p tcp -d $IP --dport 389 -j ACCEPT +fi + + -- cgit From bc2641148359352ed83d4190baaf1e208e00a6b9 Mon Sep 17 00:00:00 2001 From: Devin Carlen Date: Wed, 15 Sep 2010 17:49:15 -0700 Subject: Added iptables host initial configuration --- nova/network/manager.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/nova/network/manager.py b/nova/network/manager.py index 87c3d8e46..25a9ba474 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -219,10 +219,11 @@ class FlatManager(NetworkManager): class VlanManager(NetworkManager): """Vlan network with dhcp""" - def init_host(self): - """Do any initialization that needs to be run if this is a standalone service. - """ - driver.init_host() + def init_host(self): + """Do any initialization that needs to be run if this is a + standalone service. + """ + driver.init_host() def allocate_fixed_ip(self, context, instance_id, *args, **kwargs): """Gets a fixed ip from the pool""" -- cgit From e1ddec70bc7522a75b4a50953a0f4b20ace6cce1 Mon Sep 17 00:00:00 2001 From: Devin Carlen Date: Thu, 16 Sep 2010 11:40:04 -0700 Subject: Added missing masquerade rules --- nova/network/linux_net.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index 604d11c93..75acf2afc 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -58,6 +58,8 @@ def init_host(): "-j SNAT --to-source %s" % (FLAGS.private_range, FLAGS.routing_source_ip)) + _confirm_rule("-A nova_postrouting -s %s MASQUERADE" % FLAGS.private_range) + _confirm_rule("-A nova_postrouting -s %(range)s -d %(range)s" % {'range': FLAGS.private_range}) def bind_floating_ip(floating_ip): """Bind ip to public interface""" -- cgit From d0708205759880e7fb78fbb1df33df939f669413 Mon Sep 17 00:00:00 2001 From: Devin Carlen Date: Thu, 16 Sep 2010 11:44:51 -0700 Subject: Whitespace fixes --- bin/nova-manage | 1 - nova/manager.py | 3 --- 2 files changed, 4 deletions(-) diff --git a/bin/nova-manage b/bin/nova-manage index 909435ede..325245ac4 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -384,4 +384,3 @@ def main(): if __name__ == '__main__': main() - diff --git a/nova/manager.py b/nova/manager.py index 495b1f0d1..b7b97bced 100644 --- a/nova/manager.py +++ b/nova/manager.py @@ -44,6 +44,3 @@ class Manager(object): Child classes should override this method. """ - - - -- cgit From 68633fadeb92a5a26d1ab613bed6094ddfa2a014 Mon Sep 17 00:00:00 2001 From: Devin Carlen Date: Mon, 20 Sep 2010 15:35:44 -0700 Subject: Whitespace fixes --- nova/endpoint/api.py | 1 - nova/flags.py | 1 - nova/network/linux_net.py | 1 - nova/network/manager.py | 1 - nova/service.py | 1 - 5 files changed, 5 deletions(-) diff --git a/nova/endpoint/api.py b/nova/endpoint/api.py index 6de3698e1..56481b2c0 100755 --- a/nova/endpoint/api.py +++ b/nova/endpoint/api.py @@ -340,4 +340,3 @@ class APIServerApplication(tornado.web.Application): (r'/1.0/([-A-Za-z0-9/]*)', MetadataRequestHandler), ], pool=multiprocessing.Pool(4)) self.controllers = controllers - diff --git a/nova/flags.py b/nova/flags.py index 55b452fc3..ce30d5033 100644 --- a/nova/flags.py +++ b/nova/flags.py @@ -222,4 +222,3 @@ DEFINE_string('host', socket.gethostname(), # UNUSED DEFINE_string('node_availability_zone', 'nova', 'availability zone of this node') - diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index 75acf2afc..65dcf51ee 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -273,4 +273,3 @@ def _dnsmasq_pid_for(vlan): if os.path.exists(pid_file): with open(pid_file, 'r') as f: return int(f.read()) - diff --git a/nova/network/manager.py b/nova/network/manager.py index 25a9ba474..c7bcfa175 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -370,4 +370,3 @@ class VlanManager(NetworkManager): parent_reserved = super(VlanManager, self)._top_reserved_ips return parent_reserved + FLAGS.cnt_vpn_clients - diff --git a/nova/service.py b/nova/service.py index 8f1db1b8e..870dd6ceb 100644 --- a/nova/service.py +++ b/nova/service.py @@ -158,4 +158,3 @@ class Service(object, service.Service): self.model_disconnected = True logging.exception("model server went away") yield - -- cgit From e74b8070f73d8bada01cfe2d26223e5180ab67fb Mon Sep 17 00:00:00 2001 From: Devin Carlen Date: Tue, 21 Sep 2010 00:03:53 -0700 Subject: Renamed cc_ip flag to cc_host --- nova/flags.py | 4 ++-- nova/network/linux_net.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/nova/flags.py b/nova/flags.py index ce30d5033..9d27d336d 100644 --- a/nova/flags.py +++ b/nova/flags.py @@ -184,9 +184,9 @@ DEFINE_string('rabbit_userid', 'guest', 'rabbit userid') DEFINE_string('rabbit_password', 'guest', 'rabbit password') DEFINE_string('rabbit_virtual_host', '/', 'rabbit virtual host') DEFINE_string('control_exchange', 'nova', 'the main exchange to connect to') -DEFINE_string('cc_ip', '127.0.0.1', 'ip of api server') +DEFINE_string('cc_host', '127.0.0.1', 'ip of api server') DEFINE_integer('cc_port', 8773, 'cloud controller port') -DEFINE_string('ec2_url', 'http://%s:%s/services/Cloud' % (FLAGS.cc_ip, FLAGS.cc_port), +DEFINE_string('ec2_url', 'http://%s:%s/services/Cloud' % (FLAGS.cc_host, FLAGS.cc_port), 'Url to ec2 api server') DEFINE_string('default_image', 'ami-11111', diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index 65dcf51ee..53fb2df94 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -50,7 +50,7 @@ def init_host(): # forwarding entries and a default DNAT entry. _confirm_rule("-t nat -A nova_prerouting -s 0.0.0.0/0 " "-d 169.254.169.254/32 -p tcp -m tcp --dport 80 -j DNAT " - "--to-destination %s:%s" % (FLAGS.cc_ip, FLAGS.cc_port)) + "--to-destination %s:%s" % (FLAGS.cc_host, FLAGS.cc_port)) # NOTE(devcamcar): Cloud public SNAT entries and the default # SNAT rule for outbound traffic. -- cgit From b68ab98d6718d5a7237f5620e8caffc770dfe822 Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Tue, 21 Sep 2010 19:24:19 -0400 Subject: User updatable name & description for images. --- nova/endpoint/cloud.py | 12 ++++++++++++ nova/endpoint/images.py | 6 ++++++ nova/objectstore/handler.py | 19 +++++++++++++++---- nova/objectstore/image.py | 12 ++++++++++++ nova/tests/cloud_unittest.py | 26 +++++++++++++++++++++++++- nova/tests/objectstore_unittest.py | 6 ++++++ 6 files changed, 76 insertions(+), 5 deletions(-) diff --git a/nova/endpoint/cloud.py b/nova/endpoint/cloud.py index e29d09a89..b1678ecce 100644 --- a/nova/endpoint/cloud.py +++ b/nova/endpoint/cloud.py @@ -752,3 +752,15 @@ class CloudController(object): raise exception.ApiError('operation_type must be add or remove') result = images.modify(context, image_id, operation_type) return defer.succeed(result) + + @rbac.allow('projectmanager', 'sysadmin') + def set_image_name(self, context, image_id, name): + result = images.update_user_editable_field(context, image_id, + 'displayName', name) + return defer.succeed(result) + + @rbac.allow('projectmanager', 'sysadmin') + def set_image_description(self, context, image_id, name): + result = images.update_user_editable_field(context, image_id, + 'displayDescription', name) + return defer.succeed(result) diff --git a/nova/endpoint/images.py b/nova/endpoint/images.py index 4579cd81a..a63c059bb 100644 --- a/nova/endpoint/images.py +++ b/nova/endpoint/images.py @@ -43,6 +43,12 @@ def modify(context, image_id, operation): return True +def update_user_editable_field(context, image_id, field, value): + conn(context).make_request( + method='POST', + bucket='_images', + query_args=qs({'image_id': image_id, 'field': field, 'value': value})) + return True def register(context, image_location): """ rpc call to register a new image based from a manifest """ diff --git a/nova/objectstore/handler.py b/nova/objectstore/handler.py index 5c3dc286b..f4596e982 100644 --- a/nova/objectstore/handler.py +++ b/nova/objectstore/handler.py @@ -382,15 +382,26 @@ class ImagesResource(resource.Resource): def render_POST(self, request): # pylint: disable-msg=R0201 """Update image attributes: public/private""" + # image_id required for all requests image_id = get_argument(request, 'image_id', u'') - operation = get_argument(request, 'operation', u'') - image_object = image.Image(image_id) - if not image_object.is_authorized(request.context): + logging.debug("not authorized for handle_POST in images") raise exception.NotAuthorized - image_object.set_public(operation=='add') + operation = get_argument(request, 'operation', u'') + field = get_argument(request, 'field', u'') + value = get_argument(request, 'value', u'') + if operation: + # operation implies publicity toggle + logging.debug("handling publicity toggle") + image_object.set_public(operation=='add') + elif field: + # field implies user field editing (value can be blank) + logging.debug("update user field") + image_object.update_user_editable_field(field, value) + else: + logging.debug("unknown action for handle_POST in images") return '' diff --git a/nova/objectstore/image.py b/nova/objectstore/image.py index f3c02a425..ad1745af6 100644 --- a/nova/objectstore/image.py +++ b/nova/objectstore/image.py @@ -82,6 +82,18 @@ class Image(object): with open(os.path.join(self.path, 'info.json'), 'w') as f: json.dump(md, f) + def update_user_editable_field(self, field, value): + fields = ['displayName', 'displayDescription'] + if field not in fields: + raise KeyError("Invalid field: %s" % field) + info = self.metadata + if value: + info[field] = value + elif field in info: + del info[field] + with open(os.path.join(self.path, 'info.json'), 'w') as f: + json.dump(info, f) + @staticmethod def all(): images = [] diff --git a/nova/tests/cloud_unittest.py b/nova/tests/cloud_unittest.py index c36d5a34f..2e97180be 100644 --- a/nova/tests/cloud_unittest.py +++ b/nova/tests/cloud_unittest.py @@ -16,9 +16,13 @@ # License for the specific language governing permissions and limitations # under the License. +import json import logging +import os import StringIO +import tempfile import time + from tornado import ioloop from twisted.internet import defer import unittest @@ -32,15 +36,22 @@ from nova.auth import manager from nova.compute import power_state from nova.endpoint import api from nova.endpoint import cloud +from nova.objectstore import image FLAGS = flags.FLAGS +# Temp dirs for working with image attributes through the cloud controller +# (stole this from objectstore_unittest.py) +OSS_TEMPDIR = tempfile.mkdtemp(prefix='test_oss-') +IMAGES_PATH = os.path.join(OSS_TEMPDIR, 'images') +os.makedirs(IMAGES_PATH) + class CloudTestCase(test.BaseTestCase): def setUp(self): super(CloudTestCase, self).setUp() - self.flags(connection_type='fake') + self.flags(connection_type='fake', images_path=IMAGES_PATH) self.conn = rpc.Connection.instance() logging.getLogger().setLevel(logging.DEBUG) @@ -156,3 +167,16 @@ class CloudTestCase(test.BaseTestCase): #for i in xrange(4): # data = self.cloud.get_metadata(instance(i)['private_dns_name']) # self.assert_(data['meta-data']['ami-id'] == 'ami-%s' % i) + + def test_user_editable_endpoint(self): + pathdir = os.path.join(FLAGS.images_path, 'i-testing') + os.mkdir(pathdir) + info = {} + with open(os.path.join(pathdir, 'info.json'), 'w') as f: + json.dump(info, f) + yield self.cloud.set_image_description(self.context, 'i-testing', + 'Foo Img') + img = image.Image('i-testing') + self.assertEqual('Foo Img', img.metadata['displayDescription']) + self.cloud.set_image_description(self.context, 'i-testing', '') + self.assert_(not 'displayDescription' in img.metadata) diff --git a/nova/tests/objectstore_unittest.py b/nova/tests/objectstore_unittest.py index dece4b5d5..82c6e2c49 100644 --- a/nova/tests/objectstore_unittest.py +++ b/nova/tests/objectstore_unittest.py @@ -164,6 +164,12 @@ class ObjectStoreTestCase(test.BaseTestCase): self.context.project = self.auth_manager.get_project('proj2') self.assertFalse(my_img.is_authorized(self.context)) + # change user-editable fields + my_img.update_user_editable_field('displayName', 'my cool image') + self.assertEqual('my cool image', my_img.metadata['displayName']) + my_img.update_user_editable_field('displayName', '') + self.assert_(not 'displayName' in my_img.metadata) + class TestHTTPChannel(http.HTTPChannel): """Dummy site required for twisted.web""" -- cgit From 71c41338e4aac98ec03eec1c90ee99e43d51bcb7 Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Wed, 22 Sep 2010 16:42:35 -0400 Subject: Add user display fields to instances & volumes. --- nova/db/sqlalchemy/models.py | 9 ++++++++- nova/endpoint/cloud.py | 32 ++++++++++++++++++++++++++++++++ nova/tests/cloud_unittest.py | 43 ++++++++++++++++++++++++++++++++++++++----- 3 files changed, 78 insertions(+), 6 deletions(-) diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index 41013f41b..359087db0 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -233,7 +233,6 @@ class Instance(BASE, NovaBase): vcpus = Column(Integer) local_gb = Column(Integer) - hostname = Column(String(255)) host = Column(String(255)) # , ForeignKey('hosts.id')) @@ -247,6 +246,10 @@ class Instance(BASE, NovaBase): scheduled_at = Column(DateTime) launched_at = Column(DateTime) terminated_at = Column(DateTime) + + display_name = Column(String(255)) + display_description = Column(String(255)) + # TODO(vish): see Ewan's email about state improvements, probably # should be in a driver base class or some such # vmstate_state = running, halted, suspended, paused @@ -282,6 +285,10 @@ class Volume(BASE, NovaBase): launched_at = Column(DateTime) terminated_at = Column(DateTime) + display_name = Column(String(255)) + display_description = Column(String(255)) + + class Quota(BASE, NovaBase): """Represents quota overrides for a project""" __tablename__ = 'quotas' diff --git a/nova/endpoint/cloud.py b/nova/endpoint/cloud.py index b1678ecce..50fdee60b 100644 --- a/nova/endpoint/cloud.py +++ b/nova/endpoint/cloud.py @@ -280,6 +280,10 @@ class CloudController(object): 'volume_id': volume['str_id']}] else: v['attachmentSet'] = [{}] + if 'display_name' in volume: + v['display_name'] = volume['display_name'] + if 'display_description' in volume: + v['display_description'] = volume['display_description'] return v @rbac.allow('projectmanager', 'sysadmin') @@ -299,6 +303,8 @@ class CloudController(object): vol['availability_zone'] = FLAGS.storage_availability_zone vol['status'] = "creating" vol['attach_status'] = "detached" + vol['display_name'] = kwargs.get('display_name') + vol['display_description'] = kwargs.get('display_description') volume_ref = db.volume_create(context, vol) rpc.cast(FLAGS.scheduler_topic, @@ -367,6 +373,17 @@ class CloudController(object): lst = [lst] return [{label: x} for x in lst] + @rbac.allow('projectmanager', 'sysadmin') + def update_volume(self, context, volume_id, **kwargs): + updatable_fields = ['display_name', 'display_description'] + changes = {} + for field in updatable_fields: + if field in kwargs: + changes[field] = kwargs[field] + if changes: + db.volume_update(context, volume_id, kwargs) + return defer.succeed(True) + @rbac.allow('all') def describe_instances(self, context, **kwargs): return defer.succeed(self._format_describe_instances(context)) @@ -420,6 +437,8 @@ class CloudController(object): i['instanceType'] = instance['instance_type'] i['launchTime'] = instance['created_at'] i['amiLaunchIndex'] = instance['launch_index'] + i['displayName'] = instance['display_name'] + i['displayDescription'] = instance['display_description'] if not reservations.has_key(instance['reservation_id']): r = {} r['reservationId'] = instance['reservation_id'] @@ -589,6 +608,8 @@ class CloudController(object): base_options['user_data'] = kwargs.get('user_data', '') base_options['security_group'] = security_group base_options['instance_type'] = instance_type + base_options['display_name'] = kwargs.get('display_name') + base_options['display_description'] = kwargs.get('display_description') type_data = INSTANCE_TYPES[instance_type] base_options['memory_mb'] = type_data['memory_mb'] @@ -689,6 +710,17 @@ class CloudController(object): "instance_id": instance_ref['id']}}) return defer.succeed(True) + @rbac.allow('projectmanager', 'sysadmin') + def update_instance(self, context, instance_id, **kwargs): + updatable_fields = ['display_name', 'display_description'] + changes = {} + for field in updatable_fields: + if field in kwargs: + changes[field] = kwargs[field] + if changes: + db.instance_update(context, instance_id, kwargs) + return defer.succeed(True) + @rbac.allow('projectmanager', 'sysadmin') def delete_volume(self, context, volume_id, **kwargs): # TODO: return error if not authorized diff --git a/nova/tests/cloud_unittest.py b/nova/tests/cloud_unittest.py index 2e97180be..18ad19ede 100644 --- a/nova/tests/cloud_unittest.py +++ b/nova/tests/cloud_unittest.py @@ -28,6 +28,7 @@ from twisted.internet import defer import unittest from xml.etree import ElementTree +from nova import db from nova import flags from nova import rpc from nova import test @@ -168,15 +169,47 @@ class CloudTestCase(test.BaseTestCase): # data = self.cloud.get_metadata(instance(i)['private_dns_name']) # self.assert_(data['meta-data']['ami-id'] == 'ami-%s' % i) - def test_user_editable_endpoint(self): - pathdir = os.path.join(FLAGS.images_path, 'i-testing') + def test_user_editable_image_endpoint(self): + pathdir = os.path.join(FLAGS.images_path, 'ami-testing') os.mkdir(pathdir) info = {} with open(os.path.join(pathdir, 'info.json'), 'w') as f: json.dump(info, f) - yield self.cloud.set_image_description(self.context, 'i-testing', + yield self.cloud.set_image_description(self.context, 'ami-testing', 'Foo Img') - img = image.Image('i-testing') + img = image.Image('ami-testing') self.assertEqual('Foo Img', img.metadata['displayDescription']) - self.cloud.set_image_description(self.context, 'i-testing', '') + self.cloud.set_image_description(self.context, 'ami-testing', '') self.assert_(not 'displayDescription' in img.metadata) + + def test_update_of_instance_display_fields(self): + inst = db.instance_create({}, {}) + self.cloud.update_instance(self.context, inst['id'], + display_name='c00l 1m4g3') + inst = db.instance_get({}, inst['id']) + self.assertEqual('c00l 1m4g3', inst['display_name']) + db.instance_destroy({}, inst['id']) + + def test_update_of_instance_wont_update_private_fields(self): + inst = db.instance_create({}, {}) + self.cloud.update_instance(self.context, inst['id'], + mac_address='DE:AD:BE:EF') + inst = db.instance_get({}, inst['id']) + self.assertEqual(None, inst['mac_address']) + db.instance_destroy({}, inst['id']) + + def test_update_of_volume_display_fields(self): + vol = db.volume_create({}, {}) + self.cloud.update_volume(self.context, vol['id'], + display_name='c00l v0lum3') + vol = db.volume_get({}, vol['id']) + self.assertEqual('c00l v0lum3', vol['display_name']) + db.volume_destroy({}, vol['id']) + + def test_update_of_volume_wont_update_private_fields(self): + vol = db.volume_create({}, {}) + self.cloud.update_volume(self.context, vol['id'], + mountpoint='/not/here') + vol = db.volume_get({}, vol['id']) + self.assertEqual(None, vol['mountpoint']) + db.volume_destroy({}, vol['id']) -- cgit From 4f2edd43ca2c4a175b4d9dce23ae9e28941122e2 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Thu, 23 Sep 2010 11:23:32 -0700 Subject: renamed ipchains to iptables --- setup_iptables.sh | 94 +++++++++++++++++++++++++++++++++++++++++++++++++ tools/setup_ipchains.sh | 94 ------------------------------------------------- 2 files changed, 94 insertions(+), 94 deletions(-) create mode 100644 setup_iptables.sh delete mode 100644 tools/setup_ipchains.sh diff --git a/setup_iptables.sh b/setup_iptables.sh new file mode 100644 index 000000000..b1ab1c6f7 --- /dev/null +++ b/setup_iptables.sh @@ -0,0 +1,94 @@ +#!/usr/bin/env bash + +CMD="global" +IP="XXX" +PRIVATE_RANGE="10.128.0.0/12" + +if [ -n "$1" ]; then + CMD=$1 +fi + +if [ -n "$2" ]; then + IP=$2 +fi + +if [ -n "$3" ]; then + PRIVATE_RANGE=$3 +fi + +if [ "$CMD" == "global" ]; then + iptables -P INPUT DROP + iptables -A INPUT -m state --state INVALID -j DROP + iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT + iptables -A INPUT -m tcp -p tcp -d $MGMT_IP --dport 22 -j ACCEPT + iptables -A INPUT -m udp -p udp --dport 123 -j ACCEPT + iptables -N nova_input + iptables -A INPUT -j nova_input + iptables -A INPUT -p icmp -j ACCEPT + iptables -A INPUT -p tcp -j REJECT --reject-with tcp-reset + iptables -A INPUT -j REJECT --reject-with icmp-port-unreachable + + iptables -P FORWARD DROP + iptables -A FORWARD -m state --state INVALID -j DROP + iptables -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT + iptables -A FORWARD -p tcp -m tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu + iptables -N nova_forward + iptables -A FORWARD -j nova_forward + + iptables -P OUTPUT DROP + iptables -A OUTPUT -m state --state INVALID -j DROP + iptables -A OUTPUT -m state --state RELATED,ESTABLISHED -j ACCEPT + iptables -N nova_output + iptables -A OUTPUT -j nova_output + + iptables -t nat -N nova_prerouting + iptables -t nat -A PREROUTING -j nova_prerouting + + iptables -t nat -N nova_postrouting + iptables -t nat -A POSTROUTING -j nova_postrouting + + iptables -t nat -N nova_output + iptables -t nat -A OUTPUT -j nova_output + + # ganglia (all hosts) + iptables -A nova_input -m tcp -p tcp -d $IP --dport 8649 -j ACCEPT + iptables -A nova_input -m udp -p udp -d $IP --dport 8649 -j ACCEPT +fi + +if [ "$CMD" == "dashboard" ]; then + # dashboard + iptables -A nova_input -m tcp -p tcp -d $IP --dport 80 -j ACCEPT + iptables -A nova_input -m tcp -p tcp -d $IP --dport 443 -j ACCEPT +fi + +if [ "$CMD" == "objectstore" ]; then + iptables -A nova_input -m tcp -p tcp -d $IP --dport 3333 -j ACCEPT + iptables -A nova_input -m tcp -p tcp -d $IP --dport 8773 -j ACCEPT +fi + +if [ "$CMD" == "redis" ]; then + iptables -A nova_input -m tcp -p tcp -d $IP --dport 6379 -j ACCEPT +fi + +if [ "$CMD" == "mysql" ]; then + iptables -A nova_input -m tcp -p tcp -d $IP --dport 3306 -j ACCEPT +fi + +if [ "$CMD" == "rabbitmq" ]; then + iptables -A nova_input -m tcp -p tcp -d $IP --dport 4369 -j ACCEPT + iptables -A nova_input -m tcp -p tcp -d $IP --dport 5672 -j ACCEPT + iptables -A nova_input -m tcp -p tcp -d $IP --dport 53284 -j ACCEPT +fi + +if [ "$CMD" == "dnsmasq" ]; then + # NOTE(vish): this could theoretically be setup per network + # for each host, but it seems like overkill + iptables -A nova_input -m tcp -p tcp -s $PRIVATE_RANGE --dport 53 -j ACCEPT + iptables -A nova_input -m udp -p udp -s $PRIVATE_RANGE --dport 53 -j ACCEPT + iptables -A nova_input -m udp -p udp --dport 67 -j ACCEPT + +if [ "$CMD" == "ldap" ]; then + iptables -A nova_input -m tcp -p tcp -d $IP --dport 389 -j ACCEPT +fi + + diff --git a/tools/setup_ipchains.sh b/tools/setup_ipchains.sh deleted file mode 100644 index b1ab1c6f7..000000000 --- a/tools/setup_ipchains.sh +++ /dev/null @@ -1,94 +0,0 @@ -#!/usr/bin/env bash - -CMD="global" -IP="XXX" -PRIVATE_RANGE="10.128.0.0/12" - -if [ -n "$1" ]; then - CMD=$1 -fi - -if [ -n "$2" ]; then - IP=$2 -fi - -if [ -n "$3" ]; then - PRIVATE_RANGE=$3 -fi - -if [ "$CMD" == "global" ]; then - iptables -P INPUT DROP - iptables -A INPUT -m state --state INVALID -j DROP - iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT - iptables -A INPUT -m tcp -p tcp -d $MGMT_IP --dport 22 -j ACCEPT - iptables -A INPUT -m udp -p udp --dport 123 -j ACCEPT - iptables -N nova_input - iptables -A INPUT -j nova_input - iptables -A INPUT -p icmp -j ACCEPT - iptables -A INPUT -p tcp -j REJECT --reject-with tcp-reset - iptables -A INPUT -j REJECT --reject-with icmp-port-unreachable - - iptables -P FORWARD DROP - iptables -A FORWARD -m state --state INVALID -j DROP - iptables -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT - iptables -A FORWARD -p tcp -m tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu - iptables -N nova_forward - iptables -A FORWARD -j nova_forward - - iptables -P OUTPUT DROP - iptables -A OUTPUT -m state --state INVALID -j DROP - iptables -A OUTPUT -m state --state RELATED,ESTABLISHED -j ACCEPT - iptables -N nova_output - iptables -A OUTPUT -j nova_output - - iptables -t nat -N nova_prerouting - iptables -t nat -A PREROUTING -j nova_prerouting - - iptables -t nat -N nova_postrouting - iptables -t nat -A POSTROUTING -j nova_postrouting - - iptables -t nat -N nova_output - iptables -t nat -A OUTPUT -j nova_output - - # ganglia (all hosts) - iptables -A nova_input -m tcp -p tcp -d $IP --dport 8649 -j ACCEPT - iptables -A nova_input -m udp -p udp -d $IP --dport 8649 -j ACCEPT -fi - -if [ "$CMD" == "dashboard" ]; then - # dashboard - iptables -A nova_input -m tcp -p tcp -d $IP --dport 80 -j ACCEPT - iptables -A nova_input -m tcp -p tcp -d $IP --dport 443 -j ACCEPT -fi - -if [ "$CMD" == "objectstore" ]; then - iptables -A nova_input -m tcp -p tcp -d $IP --dport 3333 -j ACCEPT - iptables -A nova_input -m tcp -p tcp -d $IP --dport 8773 -j ACCEPT -fi - -if [ "$CMD" == "redis" ]; then - iptables -A nova_input -m tcp -p tcp -d $IP --dport 6379 -j ACCEPT -fi - -if [ "$CMD" == "mysql" ]; then - iptables -A nova_input -m tcp -p tcp -d $IP --dport 3306 -j ACCEPT -fi - -if [ "$CMD" == "rabbitmq" ]; then - iptables -A nova_input -m tcp -p tcp -d $IP --dport 4369 -j ACCEPT - iptables -A nova_input -m tcp -p tcp -d $IP --dport 5672 -j ACCEPT - iptables -A nova_input -m tcp -p tcp -d $IP --dport 53284 -j ACCEPT -fi - -if [ "$CMD" == "dnsmasq" ]; then - # NOTE(vish): this could theoretically be setup per network - # for each host, but it seems like overkill - iptables -A nova_input -m tcp -p tcp -s $PRIVATE_RANGE --dport 53 -j ACCEPT - iptables -A nova_input -m udp -p udp -s $PRIVATE_RANGE --dport 53 -j ACCEPT - iptables -A nova_input -m udp -p udp --dport 67 -j ACCEPT - -if [ "$CMD" == "ldap" ]; then - iptables -A nova_input -m tcp -p tcp -d $IP --dport 389 -j ACCEPT -fi - - -- cgit From 44a3fe22d72f7359f57e7eb9ce443c974391991c Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Thu, 23 Sep 2010 11:40:09 -0700 Subject: fixed a couple of typos --- nova/manager.py | 3 ++- nova/network/manager.py | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/nova/manager.py b/nova/manager.py index b7b97bced..65300354b 100644 --- a/nova/manager.py +++ b/nova/manager.py @@ -40,7 +40,8 @@ class Manager(object): def init_host(self): """Do any initialization that needs to be run if this is a standalone service. - + Child classes should override this method. """ + pass diff --git a/nova/network/manager.py b/nova/network/manager.py index dcca21127..abe4dcebc 100644 --- a/nova/network/manager.py +++ b/nova/network/manager.py @@ -218,12 +218,12 @@ class FlatManager(NetworkManager): class VlanManager(NetworkManager): """Vlan network with dhcp""" - + def init_host(self): """Do any initialization that needs to be run if this is a standalone service. """ - driver.init_host() + self.driver.init_host() def allocate_fixed_ip(self, context, instance_id, *args, **kwargs): """Gets a fixed ip from the pool""" -- cgit From 47a957acb176d108aac4183cbf5a882149d7462d Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Thu, 23 Sep 2010 11:58:33 -0700 Subject: put setup_iptables in the right dir --- setup_iptables.sh | 94 ------------------------------------------------- tools/setup_iptables.sh | 94 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+), 94 deletions(-) delete mode 100644 setup_iptables.sh create mode 100644 tools/setup_iptables.sh diff --git a/setup_iptables.sh b/setup_iptables.sh deleted file mode 100644 index b1ab1c6f7..000000000 --- a/setup_iptables.sh +++ /dev/null @@ -1,94 +0,0 @@ -#!/usr/bin/env bash - -CMD="global" -IP="XXX" -PRIVATE_RANGE="10.128.0.0/12" - -if [ -n "$1" ]; then - CMD=$1 -fi - -if [ -n "$2" ]; then - IP=$2 -fi - -if [ -n "$3" ]; then - PRIVATE_RANGE=$3 -fi - -if [ "$CMD" == "global" ]; then - iptables -P INPUT DROP - iptables -A INPUT -m state --state INVALID -j DROP - iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT - iptables -A INPUT -m tcp -p tcp -d $MGMT_IP --dport 22 -j ACCEPT - iptables -A INPUT -m udp -p udp --dport 123 -j ACCEPT - iptables -N nova_input - iptables -A INPUT -j nova_input - iptables -A INPUT -p icmp -j ACCEPT - iptables -A INPUT -p tcp -j REJECT --reject-with tcp-reset - iptables -A INPUT -j REJECT --reject-with icmp-port-unreachable - - iptables -P FORWARD DROP - iptables -A FORWARD -m state --state INVALID -j DROP - iptables -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT - iptables -A FORWARD -p tcp -m tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu - iptables -N nova_forward - iptables -A FORWARD -j nova_forward - - iptables -P OUTPUT DROP - iptables -A OUTPUT -m state --state INVALID -j DROP - iptables -A OUTPUT -m state --state RELATED,ESTABLISHED -j ACCEPT - iptables -N nova_output - iptables -A OUTPUT -j nova_output - - iptables -t nat -N nova_prerouting - iptables -t nat -A PREROUTING -j nova_prerouting - - iptables -t nat -N nova_postrouting - iptables -t nat -A POSTROUTING -j nova_postrouting - - iptables -t nat -N nova_output - iptables -t nat -A OUTPUT -j nova_output - - # ganglia (all hosts) - iptables -A nova_input -m tcp -p tcp -d $IP --dport 8649 -j ACCEPT - iptables -A nova_input -m udp -p udp -d $IP --dport 8649 -j ACCEPT -fi - -if [ "$CMD" == "dashboard" ]; then - # dashboard - iptables -A nova_input -m tcp -p tcp -d $IP --dport 80 -j ACCEPT - iptables -A nova_input -m tcp -p tcp -d $IP --dport 443 -j ACCEPT -fi - -if [ "$CMD" == "objectstore" ]; then - iptables -A nova_input -m tcp -p tcp -d $IP --dport 3333 -j ACCEPT - iptables -A nova_input -m tcp -p tcp -d $IP --dport 8773 -j ACCEPT -fi - -if [ "$CMD" == "redis" ]; then - iptables -A nova_input -m tcp -p tcp -d $IP --dport 6379 -j ACCEPT -fi - -if [ "$CMD" == "mysql" ]; then - iptables -A nova_input -m tcp -p tcp -d $IP --dport 3306 -j ACCEPT -fi - -if [ "$CMD" == "rabbitmq" ]; then - iptables -A nova_input -m tcp -p tcp -d $IP --dport 4369 -j ACCEPT - iptables -A nova_input -m tcp -p tcp -d $IP --dport 5672 -j ACCEPT - iptables -A nova_input -m tcp -p tcp -d $IP --dport 53284 -j ACCEPT -fi - -if [ "$CMD" == "dnsmasq" ]; then - # NOTE(vish): this could theoretically be setup per network - # for each host, but it seems like overkill - iptables -A nova_input -m tcp -p tcp -s $PRIVATE_RANGE --dport 53 -j ACCEPT - iptables -A nova_input -m udp -p udp -s $PRIVATE_RANGE --dport 53 -j ACCEPT - iptables -A nova_input -m udp -p udp --dport 67 -j ACCEPT - -if [ "$CMD" == "ldap" ]; then - iptables -A nova_input -m tcp -p tcp -d $IP --dport 389 -j ACCEPT -fi - - diff --git a/tools/setup_iptables.sh b/tools/setup_iptables.sh new file mode 100644 index 000000000..b1ab1c6f7 --- /dev/null +++ b/tools/setup_iptables.sh @@ -0,0 +1,94 @@ +#!/usr/bin/env bash + +CMD="global" +IP="XXX" +PRIVATE_RANGE="10.128.0.0/12" + +if [ -n "$1" ]; then + CMD=$1 +fi + +if [ -n "$2" ]; then + IP=$2 +fi + +if [ -n "$3" ]; then + PRIVATE_RANGE=$3 +fi + +if [ "$CMD" == "global" ]; then + iptables -P INPUT DROP + iptables -A INPUT -m state --state INVALID -j DROP + iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT + iptables -A INPUT -m tcp -p tcp -d $MGMT_IP --dport 22 -j ACCEPT + iptables -A INPUT -m udp -p udp --dport 123 -j ACCEPT + iptables -N nova_input + iptables -A INPUT -j nova_input + iptables -A INPUT -p icmp -j ACCEPT + iptables -A INPUT -p tcp -j REJECT --reject-with tcp-reset + iptables -A INPUT -j REJECT --reject-with icmp-port-unreachable + + iptables -P FORWARD DROP + iptables -A FORWARD -m state --state INVALID -j DROP + iptables -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT + iptables -A FORWARD -p tcp -m tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu + iptables -N nova_forward + iptables -A FORWARD -j nova_forward + + iptables -P OUTPUT DROP + iptables -A OUTPUT -m state --state INVALID -j DROP + iptables -A OUTPUT -m state --state RELATED,ESTABLISHED -j ACCEPT + iptables -N nova_output + iptables -A OUTPUT -j nova_output + + iptables -t nat -N nova_prerouting + iptables -t nat -A PREROUTING -j nova_prerouting + + iptables -t nat -N nova_postrouting + iptables -t nat -A POSTROUTING -j nova_postrouting + + iptables -t nat -N nova_output + iptables -t nat -A OUTPUT -j nova_output + + # ganglia (all hosts) + iptables -A nova_input -m tcp -p tcp -d $IP --dport 8649 -j ACCEPT + iptables -A nova_input -m udp -p udp -d $IP --dport 8649 -j ACCEPT +fi + +if [ "$CMD" == "dashboard" ]; then + # dashboard + iptables -A nova_input -m tcp -p tcp -d $IP --dport 80 -j ACCEPT + iptables -A nova_input -m tcp -p tcp -d $IP --dport 443 -j ACCEPT +fi + +if [ "$CMD" == "objectstore" ]; then + iptables -A nova_input -m tcp -p tcp -d $IP --dport 3333 -j ACCEPT + iptables -A nova_input -m tcp -p tcp -d $IP --dport 8773 -j ACCEPT +fi + +if [ "$CMD" == "redis" ]; then + iptables -A nova_input -m tcp -p tcp -d $IP --dport 6379 -j ACCEPT +fi + +if [ "$CMD" == "mysql" ]; then + iptables -A nova_input -m tcp -p tcp -d $IP --dport 3306 -j ACCEPT +fi + +if [ "$CMD" == "rabbitmq" ]; then + iptables -A nova_input -m tcp -p tcp -d $IP --dport 4369 -j ACCEPT + iptables -A nova_input -m tcp -p tcp -d $IP --dport 5672 -j ACCEPT + iptables -A nova_input -m tcp -p tcp -d $IP --dport 53284 -j ACCEPT +fi + +if [ "$CMD" == "dnsmasq" ]; then + # NOTE(vish): this could theoretically be setup per network + # for each host, but it seems like overkill + iptables -A nova_input -m tcp -p tcp -s $PRIVATE_RANGE --dport 53 -j ACCEPT + iptables -A nova_input -m udp -p udp -s $PRIVATE_RANGE --dport 53 -j ACCEPT + iptables -A nova_input -m udp -p udp --dport 67 -j ACCEPT + +if [ "$CMD" == "ldap" ]; then + iptables -A nova_input -m tcp -p tcp -d $IP --dport 389 -j ACCEPT +fi + + -- cgit From 564105a3f0087f31a879460d70e73bc358e0e8c0 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Thu, 23 Sep 2010 12:18:56 -0700 Subject: made use of nova_ chains a flag and fixed a few typos --- nova/network/linux_net.py | 46 ++++++++++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index 53fb2df94..149848750 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -41,6 +41,8 @@ flags.DEFINE_string('bridge_dev', 'eth0', 'network device for bridges') flags.DEFINE_string('routing_source_ip', utils.get_my_ip(), 'Public IP of network host') +flags.DEFINE_string('use_nova_chains', False, + 'use the nova_ routing chains instead of default') DEFAULT_PORTS = [("tcp", 80), ("tcp", 22), ("udp", 1194), ("tcp", 443)] @@ -48,18 +50,18 @@ def init_host(): """Basic networking setup goes here""" # NOTE(devcamcar): Cloud public DNAT entries, CloudPipe port # forwarding entries and a default DNAT entry. - _confirm_rule("-t nat -A nova_prerouting -s 0.0.0.0/0 " + _confirm_rule("PREROUTING", "-t nat -s 0.0.0.0/0 " "-d 169.254.169.254/32 -p tcp -m tcp --dport 80 -j DNAT " "--to-destination %s:%s" % (FLAGS.cc_host, FLAGS.cc_port)) # NOTE(devcamcar): Cloud public SNAT entries and the default # SNAT rule for outbound traffic. - _confirm_rule("-t nat -A nova_postrouting -s %s " + _confirm_rule("POSTROUTING", "-t nat -s %s " "-j SNAT --to-source %s" % (FLAGS.private_range, FLAGS.routing_source_ip)) - _confirm_rule("-A nova_postrouting -s %s MASQUERADE" % FLAGS.private_range) - _confirm_rule("-A nova_postrouting -s %(range)s -d %(range)s" % {'range': FLAGS.private_range}) + _confirm_rule("POSTROUTING", "-t nat -s %s MASQUERADE" % FLAGS.private_range) + _confirm_rule("POSTROUTING", "-t nat -s %(range)s -d %(range)s" % {'range': FLAGS.private_range}) def bind_floating_ip(floating_ip): """Bind ip to public interface""" @@ -75,37 +77,37 @@ def unbind_floating_ip(floating_ip): def ensure_vlan_forward(public_ip, port, private_ip): """Sets up forwarding rules for vlan""" - _confirm_rule("nova_forward -d %s -p udp --dport 1194 -j ACCEPT" % private_ip) + _confirm_rule("FORWARD", "-d %s -p udp --dport 1194 -j ACCEPT" % private_ip) _confirm_rule( - "nova_prerouting -t nat -d %s -p udp --dport %s -j DNAT --to %s:1194" + "PREROUTING -t nat -d %s -p udp --dport %s -j DNAT --to %s:1194" % (public_ip, port, private_ip)) def ensure_floating_forward(floating_ip, fixed_ip): """Ensure floating ip forwarding rule""" - _confirm_rule("nova_prerouting -t nat -d %s -j DNAT --to %s" + _confirm_rule("PREROUTING", "-t nat -d %s -j DNAT --to %s" % (floating_ip, fixed_ip)) - _confirm_rule("nova_postrouting -t nat -s %s -j SNAT --to %s" + _confirm_rule("POSTROUTING", "-t nat -s %s -j SNAT --to %s" % (fixed_ip, floating_ip)) # TODO(joshua): Get these from the secgroup datastore entries - _confirm_rule("nova_forward -d %s -p icmp -j ACCEPT" + _confirm_rule("FORWARD", "-d %s -p icmp -j ACCEPT" % (fixed_ip)) for (protocol, port) in DEFAULT_PORTS: _confirm_rule( - "nova_forward -d %s -p %s --dport %s -j ACCEPT" + "FORWARD -d %s -p %s --dport %s -j ACCEPT" % (fixed_ip, protocol, port)) def remove_floating_forward(floating_ip, fixed_ip): """Remove forwarding for floating ip""" - _remove_rule("nova_prerouting -t nat -d %s -j DNAT --to %s" + _remove_rule("PREROUTING", "-t nat -d %s -j DNAT --to %s" % (floating_ip, fixed_ip)) - _remove_rule("nova_postrouting -t nat -s %s -j SNAT --to %s" + _remove_rule("POSTROUTING", "-t nat -s %s -j SNAT --to %s" % (fixed_ip, floating_ip)) - _remove_rule("nova_forward -d %s -p icmp -j ACCEPT" + _remove_rule("FORWARD", "-d %s -p icmp -j ACCEPT" % (fixed_ip)) for (protocol, port) in DEFAULT_PORTS: - _remove_rule("nova_forward -d %s -p %s --dport %s -j ACCEPT" + _remove_rule("FORWARD", "-d %s -p %s --dport %s -j ACCEPT" % (fixed_ip, protocol, port)) @@ -141,7 +143,7 @@ def ensure_bridge(bridge, interface, net_attrs=None): net_attrs['gateway'], net_attrs['broadcast'], net_attrs['netmask'])) - _confirm_rule("nova_forward --in-interface %s -j ACCEPT" % bridge) + _confirm_rule("FORWARD", "--in-interface %s -j ACCEPT" % bridge) else: _execute("sudo ifconfig %s up" % bridge) @@ -211,15 +213,19 @@ def _device_exists(device): return not err -def _confirm_rule(cmd): +def _confirm_rule(chain, cmd): """Delete and re-add iptables rule""" - _execute("sudo iptables --delete %s" % (cmd), check_exit_code=False) - _execute("sudo iptables -I %s" % (cmd)) + if FLAGS.use_nova_chains: + chain = "nova_%s" % chain.lower() + _execute("sudo iptables --delete %s %s" % (chain, cmd), check_exit_code=False) + _execute("sudo iptables -I %s %s" % (chain, cmd)) -def _remove_rule(cmd): +def _remove_rule(chain, cmd): """Remove iptables rule""" - _execute("sudo iptables --delete %s" % (cmd)) + if FLAGS.use_nova_chains: + chain = "%S" % chain.lower() + _execute("sudo iptables --delete %s %s" % (chain, cmd)) def _dnsmasq_cmd(net): -- cgit From 81fc2078ca3d3e07728a39b6cdec47af871f2f2f Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Thu, 23 Sep 2010 12:20:40 -0700 Subject: removed extra line in manage --- nova/manager.py | 1 - 1 file changed, 1 deletion(-) diff --git a/nova/manager.py b/nova/manager.py index 65300354b..94e4ae959 100644 --- a/nova/manager.py +++ b/nova/manager.py @@ -44,4 +44,3 @@ class Manager(object): Child classes should override this method. """ pass - -- cgit From 065257fb0686d848fcf20235a4e04b76872a5b01 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Thu, 23 Sep 2010 12:43:41 -0700 Subject: fixed a few missing params from iptables rules --- nova/network/linux_net.py | 4 ++-- nova/service.py | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index 149848750..38a616e83 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -60,8 +60,8 @@ def init_host(): "-j SNAT --to-source %s" % (FLAGS.private_range, FLAGS.routing_source_ip)) - _confirm_rule("POSTROUTING", "-t nat -s %s MASQUERADE" % FLAGS.private_range) - _confirm_rule("POSTROUTING", "-t nat -s %(range)s -d %(range)s" % {'range': FLAGS.private_range}) + _confirm_rule("POSTROUTING", "-t nat -s %s -j MASQUERADE" % FLAGS.private_range) + _confirm_rule("POSTROUTING", "-t nat -s %(range)s -d %(range)s -j ACCEPT" % {'range': FLAGS.private_range}) def bind_floating_ip(floating_ip): """Bind ip to public interface""" diff --git a/nova/service.py b/nova/service.py index 870dd6ceb..dcd2a09ef 100644 --- a/nova/service.py +++ b/nova/service.py @@ -50,6 +50,7 @@ class Service(object, service.Service): self.topic = topic manager_class = utils.import_class(manager) self.manager = manager_class(host=host, *args, **kwargs) + self.manager.init_host() self.model_disconnected = False super(Service, self).__init__(*args, **kwargs) try: -- cgit From 15c2678d3e3899e7ab6180dce457ae6d3e54937d Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Fri, 24 Sep 2010 18:21:58 -0700 Subject: improved the shell script for iptables --- tools/setup_iptables.sh | 124 ++++++++++++++++++++++++++++++------------------ 1 file changed, 78 insertions(+), 46 deletions(-) mode change 100644 => 100755 tools/setup_iptables.sh diff --git a/tools/setup_iptables.sh b/tools/setup_iptables.sh old mode 100644 new mode 100755 index b1ab1c6f7..fd32f6f82 --- a/tools/setup_iptables.sh +++ b/tools/setup_iptables.sh @@ -1,93 +1,125 @@ #!/usr/bin/env bash - -CMD="global" -IP="XXX" -PRIVATE_RANGE="10.128.0.0/12" +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2010 United States Government as represented by the +# Administrator of the National Aeronautics and Space Administration. +# 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. if [ -n "$1" ]; then CMD=$1 +else + CMD="all" fi if [ -n "$2" ]; then IP=$2 +else + # NOTE(vish): this will just get the first ip in the list, so if you + # have more than one eth device set up, this will fail + IP=`ifconfig | grep -m 1 'inet addr:'| cut -d: -f2 | awk '{print $1}'` fi if [ -n "$3" ]; then PRIVATE_RANGE=$3 +else + PRIVATE_RANGE="10.0.0.0/12" +fi + + +if [ -n "$4" ]; then + MGMT_IP=$4 +else + MGMT_IP="$IP" fi -if [ "$CMD" == "global" ]; then - iptables -P INPUT DROP - iptables -A INPUT -m state --state INVALID -j DROP - iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT - iptables -A INPUT -m tcp -p tcp -d $MGMT_IP --dport 22 -j ACCEPT - iptables -A INPUT -m udp -p udp --dport 123 -j ACCEPT - iptables -N nova_input - iptables -A INPUT -j nova_input - iptables -A INPUT -p icmp -j ACCEPT - iptables -A INPUT -p tcp -j REJECT --reject-with tcp-reset - iptables -A INPUT -j REJECT --reject-with icmp-port-unreachable - - iptables -P FORWARD DROP - iptables -A FORWARD -m state --state INVALID -j DROP - iptables -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT - iptables -A FORWARD -p tcp -m tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu - iptables -N nova_forward - iptables -A FORWARD -j nova_forward - - iptables -P OUTPUT DROP - iptables -A OUTPUT -m state --state INVALID -j DROP - iptables -A OUTPUT -m state --state RELATED,ESTABLISHED -j ACCEPT - iptables -N nova_output - iptables -A OUTPUT -j nova_output - - iptables -t nat -N nova_prerouting - iptables -t nat -A PREROUTING -j nova_prerouting - - iptables -t nat -N nova_postrouting - iptables -t nat -A POSTROUTING -j nova_postrouting - - iptables -t nat -N nova_output - iptables -t nat -A OUTPUT -j nova_output - - # ganglia (all hosts) +iptables -F +iptables -P INPUT DROP +iptables -A INPUT -m state --state INVALID -j DROP +iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT +iptables -A INPUT -m tcp -p tcp -d $MGMT_IP --dport 22 -j ACCEPT +iptables -A INPUT -m udp -p udp --dport 123 -j ACCEPT +iptables -N nova_input +iptables -A INPUT -j nova_input +iptables -A INPUT -p icmp -j ACCEPT +iptables -A INPUT -p tcp -j REJECT --reject-with tcp-reset +iptables -A INPUT -j REJECT --reject-with icmp-port-unreachable + +iptables -P FORWARD DROP +iptables -A FORWARD -m state --state INVALID -j DROP +iptables -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT +iptables -A FORWARD -p tcp -m tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu +iptables -N nova_forward +iptables -A FORWARD -j nova_forward + +iptables -P OUTPUT DROP +iptables -A OUTPUT -m state --state INVALID -j DROP +iptables -A OUTPUT -m state --state RELATED,ESTABLISHED -j ACCEPT +iptables -N nova_output +iptables -A OUTPUT -j nova_output + +iptables -t nat -N nova_prerouting +iptables -t nat -A PREROUTING -j nova_prerouting + +iptables -t nat -N nova_postrouting +iptables -t nat -A POSTROUTING -j nova_postrouting + +iptables -t nat -N nova_output +iptables -t nat -A OUTPUT -j nova_output + +if [ "$CMD" == "ganglia" ] || [ "$CMD" == "all" ]; then iptables -A nova_input -m tcp -p tcp -d $IP --dport 8649 -j ACCEPT iptables -A nova_input -m udp -p udp -d $IP --dport 8649 -j ACCEPT fi -if [ "$CMD" == "dashboard" ]; then +if [ "$CMD" == "dashboard" ] || [ "$CMD" == "all" ]; then # dashboard iptables -A nova_input -m tcp -p tcp -d $IP --dport 80 -j ACCEPT iptables -A nova_input -m tcp -p tcp -d $IP --dport 443 -j ACCEPT fi -if [ "$CMD" == "objectstore" ]; then +if [ "$CMD" == "objectstore" ] || [ "$CMD" == "all" ]; then iptables -A nova_input -m tcp -p tcp -d $IP --dport 3333 -j ACCEPT +fi + +if [ "$CMD" == "api" ] || [ "$CMD" == "all" ]; then iptables -A nova_input -m tcp -p tcp -d $IP --dport 8773 -j ACCEPT fi -if [ "$CMD" == "redis" ]; then +if [ "$CMD" == "redis" ] || [ "$CMD" == "all" ]; then iptables -A nova_input -m tcp -p tcp -d $IP --dport 6379 -j ACCEPT fi -if [ "$CMD" == "mysql" ]; then +if [ "$CMD" == "mysql" ] || [ "$CMD" == "all" ]; then iptables -A nova_input -m tcp -p tcp -d $IP --dport 3306 -j ACCEPT fi -if [ "$CMD" == "rabbitmq" ]; then +if [ "$CMD" == "rabbitmq" ] || [ "$CMD" == "all" ]; then iptables -A nova_input -m tcp -p tcp -d $IP --dport 4369 -j ACCEPT iptables -A nova_input -m tcp -p tcp -d $IP --dport 5672 -j ACCEPT iptables -A nova_input -m tcp -p tcp -d $IP --dport 53284 -j ACCEPT fi -if [ "$CMD" == "dnsmasq" ]; then +if [ "$CMD" == "dnsmasq" ] || [ "$CMD" == "all" ]; then # NOTE(vish): this could theoretically be setup per network # for each host, but it seems like overkill iptables -A nova_input -m tcp -p tcp -s $PRIVATE_RANGE --dport 53 -j ACCEPT iptables -A nova_input -m udp -p udp -s $PRIVATE_RANGE --dport 53 -j ACCEPT iptables -A nova_input -m udp -p udp --dport 67 -j ACCEPT +fi -if [ "$CMD" == "ldap" ]; then +if [ "$CMD" == "ldap" ] || [ "$CMD" == "all" ]; then iptables -A nova_input -m tcp -p tcp -d $IP --dport 389 -j ACCEPT fi -- cgit From 41a598f09baee94125608873f4d7118000fc55ea Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Fri, 24 Sep 2010 19:57:41 -0700 Subject: add a reset command --- tools/setup_iptables.sh | 74 +++++++++++++++++++++++++++---------------------- 1 file changed, 41 insertions(+), 33 deletions(-) diff --git a/tools/setup_iptables.sh b/tools/setup_iptables.sh index fd32f6f82..7368fadf9 100755 --- a/tools/setup_iptables.sh +++ b/tools/setup_iptables.sh @@ -43,40 +43,48 @@ if [ -n "$4" ]; then else MGMT_IP="$IP" fi +if [ "$CMD" == "clear" ]; then + iptables -P INPUT ACCEPT + iptables -P FORWARD ACCEPT + iptables -P OUTPUT ACCEPT + iptables -F + iptables -X +fi -iptables -F -iptables -P INPUT DROP -iptables -A INPUT -m state --state INVALID -j DROP -iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT -iptables -A INPUT -m tcp -p tcp -d $MGMT_IP --dport 22 -j ACCEPT -iptables -A INPUT -m udp -p udp --dport 123 -j ACCEPT -iptables -N nova_input -iptables -A INPUT -j nova_input -iptables -A INPUT -p icmp -j ACCEPT -iptables -A INPUT -p tcp -j REJECT --reject-with tcp-reset -iptables -A INPUT -j REJECT --reject-with icmp-port-unreachable - -iptables -P FORWARD DROP -iptables -A FORWARD -m state --state INVALID -j DROP -iptables -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT -iptables -A FORWARD -p tcp -m tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu -iptables -N nova_forward -iptables -A FORWARD -j nova_forward - -iptables -P OUTPUT DROP -iptables -A OUTPUT -m state --state INVALID -j DROP -iptables -A OUTPUT -m state --state RELATED,ESTABLISHED -j ACCEPT -iptables -N nova_output -iptables -A OUTPUT -j nova_output - -iptables -t nat -N nova_prerouting -iptables -t nat -A PREROUTING -j nova_prerouting - -iptables -t nat -N nova_postrouting -iptables -t nat -A POSTROUTING -j nova_postrouting - -iptables -t nat -N nova_output -iptables -t nat -A OUTPUT -j nova_output +if [ "$CMD" == "base" ] || [ "$CMD" == "all" ]; then + iptables -P INPUT DROP + iptables -A INPUT -m state --state INVALID -j DROP + iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT + iptables -A INPUT -m tcp -p tcp -d $MGMT_IP --dport 22 -j ACCEPT + iptables -A INPUT -m udp -p udp --dport 123 -j ACCEPT + iptables -N nova_input + iptables -A INPUT -j nova_input + iptables -A INPUT -p icmp -j ACCEPT + iptables -A INPUT -p tcp -j REJECT --reject-with tcp-reset + iptables -A INPUT -j REJECT --reject-with icmp-port-unreachable + + iptables -P FORWARD DROP + iptables -A FORWARD -m state --state INVALID -j DROP + iptables -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT + iptables -A FORWARD -p tcp -m tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu + iptables -N nova_forward + iptables -A FORWARD -j nova_forward + + iptables -P OUTPUT DROP + iptables -A OUTPUT -m state --state INVALID -j DROP + iptables -A OUTPUT -m state --state RELATED,ESTABLISHED -j ACCEPT + iptables -N nova_output + iptables -A OUTPUT -j nova_output + + iptables -t nat -N nova_prerouting + iptables -t nat -A PREROUTING -j nova_prerouting + + iptables -t nat -N nova_postrouting + iptables -t nat -A POSTROUTING -j nova_postrouting + + iptables -t nat -N nova_output + iptables -t nat -A OUTPUT -j nova_output +fi if [ "$CMD" == "ganglia" ] || [ "$CMD" == "all" ]; then iptables -A nova_input -m tcp -p tcp -d $IP --dport 8649 -j ACCEPT -- cgit From 73cc0e446297781182fab1b8e8447e7c6d100b08 Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Fri, 24 Sep 2010 23:32:00 -0400 Subject: Update auth manager to have a update_user method and better tests. --- nova/auth/ldapdriver.py | 16 +- nova/auth/manager.py | 6 + nova/tests/auth_unittest.py | 408 +++++++++++++++++++++++++++++--------------- 3 files changed, 290 insertions(+), 140 deletions(-) diff --git a/nova/auth/ldapdriver.py b/nova/auth/ldapdriver.py index 021851ebf..640ea169e 100644 --- a/nova/auth/ldapdriver.py +++ b/nova/auth/ldapdriver.py @@ -256,8 +256,7 @@ class LdapDriver(object): if not self.__user_exists(uid): raise exception.NotFound("User %s doesn't exist" % uid) self.__remove_from_all(uid) - self.conn.delete_s('uid=%s,%s' % (uid, - FLAGS.ldap_user_subtree)) + self.conn.delete_s(self.__uid_to_dn(uid)) def delete_project(self, project_id): """Delete a project""" @@ -265,6 +264,19 @@ class LdapDriver(object): self.__delete_roles(project_dn) self.__delete_group(project_dn) + def modify_user(self, uid, access_key=None, secret_key=None, admin=None): + """Modify an existing project""" + if not access_key and not secret_key and admin is None: + return + attr = [] + if access_key: + attr.append((self.ldap.MOD_REPLACE, 'accessKey', access_key)) + if secret_key: + attr.append((self.ldap.MOD_REPLACE, 'secretKey', secret_key)) + if admin is not None: + attr.append((self.ldap.MOD_REPLACE, 'isAdmin', str(admin).upper())) + self.conn.modify_s(self.__uid_to_dn(uid), attr) + def __user_exists(self, uid): """Check if user exists""" return self.get_user(uid) != None diff --git a/nova/auth/manager.py b/nova/auth/manager.py index 55fbf42aa..0bc12c80f 100644 --- a/nova/auth/manager.py +++ b/nova/auth/manager.py @@ -630,6 +630,12 @@ class AuthManager(object): with self.driver() as drv: drv.delete_user(uid) + def modify_user(self, user, access_key=None, secret_key=None, admin=None): + """Modify credentials for a user""" + uid = User.safe_id(user) + with self.driver() as drv: + drv.modify_user(uid, access_key, secret_key, admin) + def get_credentials(self, user, project=None): """Get credential zip for user in project""" if not isinstance(user, User): diff --git a/nova/tests/auth_unittest.py b/nova/tests/auth_unittest.py index 3235dea39..7065105c3 100644 --- a/nova/tests/auth_unittest.py +++ b/nova/tests/auth_unittest.py @@ -28,25 +28,73 @@ from nova.api.ec2 import cloud FLAGS = flags.FLAGS - +class user_generator(object): + def __init__(self, manager, **user_state): + if 'name' not in user_state: + user_state['name'] = 'test1' + self.manager = manager + self.user = manager.create_user(**user_state) + + def __enter__(self): + return self.user + + def __exit__(self, value, type, trace): + self.manager.delete_user(self.user) + +class project_generator(object): + def __init__(self, manager, **project_state): + if 'name' not in project_state: + project_state['name'] = 'testproj' + if 'manager_user' not in project_state: + project_state['manager_user'] = 'test1' + self.manager = manager + self.project = manager.create_project(**project_state) + + def __enter__(self): + return self.project + + def __exit__(self, value, type, trace): + self.manager.delete_project(self.project) + +class user_and_project_generator(object): + def __init__(self, manager, user_state={}, project_state={}): + self.manager = manager + if 'name' not in user_state: + user_state['name'] = 'test1' + if 'name' not in project_state: + project_state['name'] = 'testproj' + if 'manager_user' not in project_state: + project_state['manager_user'] = 'test1' + self.user = manager.create_user(**user_state) + self.project = manager.create_project(**project_state) + + def __enter__(self): + return (self.user, self.project) + + def __exit__(self, value, type, trace): + self.manager.delete_user(self.user) + self.manager.delete_project(self.project) + +# TODO(todd): Have a test class that tests just the AuthManager functions +# and different test classes for User and Project wrappers class AuthTestCase(test.BaseTestCase): def setUp(self): super(AuthTestCase, self).setUp() self.flags(connection_type='fake') self.manager = manager.AuthManager() - def test_001_can_create_users(self): - self.manager.create_user('test1', 'access', 'secret') - self.manager.create_user('test2') - - def test_002_can_get_user(self): - user = self.manager.get_user('test1') + def test_create_and_find_user(self): + with user_generator(self.manager): + self.assert_(self.manager.get_user('test1')) - def test_003_can_retreive_properties(self): - user = self.manager.get_user('test1') - self.assertEqual('test1', user.id) - self.assertEqual('access', user.access) - self.assertEqual('secret', user.secret) + def test_create_and_find_with_properties(self): + with user_generator(self.manager, name="herbert", secret="classified", + access="private-party"): + u = self.manager.get_user('herbert') + self.assertEqual('herbert', u.id) + self.assertEqual('herbert', u.name) + self.assertEqual('classified', u.secret) + self.assertEqual('private-party', u.access) def test_004_signature_is_valid(self): #self.assertTrue(self.manager.authenticate( **boto.generate_url ... ? ? ? )) @@ -63,133 +111,217 @@ class AuthTestCase(test.BaseTestCase): 'export S3_URL="http://127.0.0.1:3333/"\n' + 'export EC2_USER_ID="test1"\n') - def test_010_can_list_users(self): - users = self.manager.get_users() - logging.warn(users) - self.assertTrue(filter(lambda u: u.id == 'test1', users)) - - def test_101_can_add_user_role(self): - self.assertFalse(self.manager.has_role('test1', 'itsec')) - self.manager.add_role('test1', 'itsec') - self.assertTrue(self.manager.has_role('test1', 'itsec')) - - def test_199_can_remove_user_role(self): - self.assertTrue(self.manager.has_role('test1', 'itsec')) - self.manager.remove_role('test1', 'itsec') - self.assertFalse(self.manager.has_role('test1', 'itsec')) - - def test_201_can_create_project(self): - project = self.manager.create_project('testproj', 'test1', 'A test project', ['test1']) - self.assertTrue(filter(lambda p: p.name == 'testproj', self.manager.get_projects())) - self.assertEqual(project.name, 'testproj') - self.assertEqual(project.description, 'A test project') - self.assertEqual(project.project_manager_id, 'test1') - self.assertTrue(project.has_member('test1')) - - def test_202_user1_is_project_member(self): - self.assertTrue(self.manager.get_user('test1').is_project_member('testproj')) - - def test_203_user2_is_not_project_member(self): - self.assertFalse(self.manager.get_user('test2').is_project_member('testproj')) - - def test_204_user1_is_project_manager(self): - self.assertTrue(self.manager.get_user('test1').is_project_manager('testproj')) - - def test_205_user2_is_not_project_manager(self): - self.assertFalse(self.manager.get_user('test2').is_project_manager('testproj')) - - def test_206_can_add_user_to_project(self): - self.manager.add_to_project('test2', 'testproj') - self.assertTrue(self.manager.get_project('testproj').has_member('test2')) - - def test_207_can_remove_user_from_project(self): - self.manager.remove_from_project('test2', 'testproj') - self.assertFalse(self.manager.get_project('testproj').has_member('test2')) - - def test_208_can_remove_add_user_with_role(self): - self.manager.add_to_project('test2', 'testproj') - self.manager.add_role('test2', 'developer', 'testproj') - self.manager.remove_from_project('test2', 'testproj') - self.assertFalse(self.manager.has_role('test2', 'developer', 'testproj')) - self.manager.add_to_project('test2', 'testproj') - self.manager.remove_from_project('test2', 'testproj') - - def test_209_can_generate_x509(self): - # MUST HAVE RUN CLOUD SETUP BY NOW - self.cloud = cloud.CloudController() - self.cloud.setup() - _key, cert_str = self.manager._generate_x509_cert('test1', 'testproj') - logging.debug(cert_str) - - # Need to verify that it's signed by the right intermediate CA - full_chain = crypto.fetch_ca(project_id='testproj', chain=True) - int_cert = crypto.fetch_ca(project_id='testproj', chain=False) - cloud_cert = crypto.fetch_ca() - logging.debug("CA chain:\n\n =====\n%s\n\n=====" % full_chain) - signed_cert = X509.load_cert_string(cert_str) - chain_cert = X509.load_cert_string(full_chain) - int_cert = X509.load_cert_string(int_cert) - cloud_cert = X509.load_cert_string(cloud_cert) - self.assertTrue(signed_cert.verify(chain_cert.get_pubkey())) - self.assertTrue(signed_cert.verify(int_cert.get_pubkey())) - - if not FLAGS.use_intermediate_ca: - self.assertTrue(signed_cert.verify(cloud_cert.get_pubkey())) - else: - self.assertFalse(signed_cert.verify(cloud_cert.get_pubkey())) - - def test_210_can_add_project_role(self): - project = self.manager.get_project('testproj') - self.assertFalse(project.has_role('test1', 'sysadmin')) - self.manager.add_role('test1', 'sysadmin') - self.assertFalse(project.has_role('test1', 'sysadmin')) - project.add_role('test1', 'sysadmin') - self.assertTrue(project.has_role('test1', 'sysadmin')) - - def test_211_can_list_project_roles(self): - project = self.manager.get_project('testproj') - user = self.manager.get_user('test1') - self.manager.add_role(user, 'netadmin', project) - roles = self.manager.get_user_roles(user) - self.assertTrue('sysadmin' in roles) - self.assertFalse('netadmin' in roles) - project_roles = self.manager.get_user_roles(user, project) - self.assertTrue('sysadmin' in project_roles) - self.assertTrue('netadmin' in project_roles) - # has role should be false because global role is missing - self.assertFalse(self.manager.has_role(user, 'netadmin', project)) - - - def test_212_can_remove_project_role(self): - project = self.manager.get_project('testproj') - self.assertTrue(project.has_role('test1', 'sysadmin')) - project.remove_role('test1', 'sysadmin') - self.assertFalse(project.has_role('test1', 'sysadmin')) - self.manager.remove_role('test1', 'sysadmin') - self.assertFalse(project.has_role('test1', 'sysadmin')) - - def test_214_can_retrieve_project_by_user(self): - project = self.manager.create_project('testproj2', 'test2', 'Another test project', ['test2']) - self.assert_(len(self.manager.get_projects()) > 1) - self.assertEqual(len(self.manager.get_projects('test2')), 1) - - def test_220_can_modify_project(self): - self.manager.modify_project('testproj', 'test2', 'new description') - project = self.manager.get_project('testproj') - self.assertEqual(project.project_manager_id, 'test2') - self.assertEqual(project.description, 'new description') - - def test_299_can_delete_project(self): - self.manager.delete_project('testproj') - self.assertFalse(filter(lambda p: p.name == 'testproj', self.manager.get_projects())) - self.manager.delete_project('testproj2') - - def test_999_can_delete_users(self): + def test_can_list_users(self): + with user_generator(self.manager): + with user_generator(self.manager, name="test2"): + users = self.manager.get_users() + self.assert_(filter(lambda u: u.id == 'test1', users)) + self.assert_(filter(lambda u: u.id == 'test2', users)) + self.assert_(not filter(lambda u: u.id == 'test3', users)) + + def test_can_add_and_remove_user_role(self): + with user_generator(self.manager): + self.assertFalse(self.manager.has_role('test1', 'itsec')) + self.manager.add_role('test1', 'itsec') + self.assertTrue(self.manager.has_role('test1', 'itsec')) + self.manager.remove_role('test1', 'itsec') + self.assertFalse(self.manager.has_role('test1', 'itsec')) + + def test_can_create_and_get_project(self): + with user_and_project_generator(self.manager) as (u,p): + self.assert_(self.manager.get_user('test1')) + self.assert_(self.manager.get_user('test1')) + self.assert_(self.manager.get_project('testproj')) + + def test_can_list_projects(self): + with user_and_project_generator(self.manager): + with project_generator(self.manager, name="testproj2"): + projects = self.manager.get_projects() + self.assert_(filter(lambda p: p.name == 'testproj', projects)) + self.assert_(filter(lambda p: p.name == 'testproj2', projects)) + self.assert_(not filter(lambda p: p.name == 'testproj3', + projects)) + + def test_can_create_and_get_project_with_attributes(self): + with user_generator(self.manager): + with project_generator(self.manager, description='A test project'): + project = self.manager.get_project('testproj') + self.assertEqual('A test project', project.description) + + def test_can_create_project_with_manager(self): + with user_and_project_generator(self.manager) as (user, project): + self.assertEqual('test1', project.project_manager_id) + self.assertTrue(user.is_project_manager('testproj')) + + def test_create_project_assigns_manager_to_members(self): + with user_and_project_generator(self.manager) as (user, project): + self.assertTrue(user.is_project_member('testproj')) + self.assertTrue(project.has_member('test1')) + + def test_no_extra_project_members(self): + with user_generator(self.manager, name='test2') as baduser: + with user_and_project_generator(self.manager) as (user, project): + self.assertFalse(baduser.is_project_member('testproj')) + self.assertFalse(project.has_member('test2')) + + def test_no_extra_project_managers(self): + with user_generator(self.manager, name='test2') as baduser: + with user_and_project_generator(self.manager) as (user, project): + self.assertFalse(baduser.is_project_manager('testproj')) + + def test_can_add_user_to_project(self): + with user_generator(self.manager, name='test2') as user: + with user_and_project_generator(self.manager): + self.manager.add_to_project('test2', 'testproj') + self.assertTrue(user.is_project_member('testproj')) + # NOTE(todd): have to reload after the add_to_project + project = self.manager.get_project('testproj') + self.assertTrue(project.has_member('test2')) + + def test_can_remove_user_from_project(self): + with user_generator(self.manager, name='test2') as user: + with user_and_project_generator(self.manager): + self.manager.add_to_project('test2', 'testproj') + self.assertTrue(user.is_project_member('testproj')) + self.manager.remove_from_project('test2', 'testproj') + self.assertFalse(user.is_project_member('testproj')) + # NOTE(todd): have to reload after the membership modifications + project = self.manager.get_project('testproj') + self.assertFalse(project.has_member('test2')) + + def test_can_add_remove_user_with_role(self): + with user_generator(self.manager, name='test2') as user: + with user_and_project_generator(self.manager): + self.manager.add_to_project('test2', 'testproj') + self.manager.add_role('test2', 'developer', 'testproj') + self.assertTrue(user.is_project_member('testproj')) + self.manager.remove_from_project('test2', 'testproj') + self.assertFalse(self.manager.has_role('test2', 'developer', + 'testproj')) + self.assertFalse(user.is_project_member('testproj')) + + def test_can_generate_x509(self): + # NOTE(todd): this doesn't assert against the auth manager + # so it probably belongs in crypto_unittest + # but I'm leaving it where I found it. + with user_and_project_generator(self.manager) as (user, project): + # NOTE(todd): Should mention why we must setup controller first + # (somebody please clue me in) + cloud_controller = cloud.CloudController() + cloud_controller.setup() + _key, cert_str = self.manager._generate_x509_cert('test1', + 'testproj') + logging.debug(cert_str) + + # Need to verify that it's signed by the right intermediate CA + full_chain = crypto.fetch_ca(project_id='testproj', chain=True) + int_cert = crypto.fetch_ca(project_id='testproj', chain=False) + cloud_cert = crypto.fetch_ca() + logging.debug("CA chain:\n\n =====\n%s\n\n=====" % full_chain) + signed_cert = X509.load_cert_string(cert_str) + chain_cert = X509.load_cert_string(full_chain) + int_cert = X509.load_cert_string(int_cert) + cloud_cert = X509.load_cert_string(cloud_cert) + self.assertTrue(signed_cert.verify(chain_cert.get_pubkey())) + self.assertTrue(signed_cert.verify(int_cert.get_pubkey())) + if not FLAGS.use_intermediate_ca: + self.assertTrue(signed_cert.verify(cloud_cert.get_pubkey())) + else: + self.assertFalse(signed_cert.verify(cloud_cert.get_pubkey())) + + def test_adding_role_to_project_is_ignored_unless_added_to_user(self): + with user_and_project_generator(self.manager) as (user, project): + self.assertFalse(project.has_role('test1', 'sysadmin')) + project.add_role('test1', 'sysadmin') + # NOTE(todd): it will still show up in get_user_roles(u, project) + self.assertFalse(project.has_role('test1', 'sysadmin')) + self.manager.add_role('test1', 'sysadmin') + self.assertTrue(project.has_role('test1', 'sysadmin')) + + def test_add_user_role_doesnt_infect_project_roles(self): + with user_and_project_generator(self.manager) as (user, project): + self.assertFalse(project.has_role('test1', 'sysadmin')) + self.manager.add_role('test1', 'sysadmin') + self.assertFalse(project.has_role('test1', 'sysadmin')) + + def test_can_list_user_roles(self): + with user_and_project_generator(self.manager) as (user, project): + self.manager.add_role('test1', 'sysadmin') + roles = self.manager.get_user_roles(user) + self.assertTrue('sysadmin' in roles) + self.assertFalse('netadmin' in roles) + + def test_can_list_project_roles(self): + with user_and_project_generator(self.manager) as (user, project): + self.manager.add_role('test1', 'sysadmin') + project.add_role('test1', 'sysadmin') + project.add_role('test1', 'netadmin') + project_roles = self.manager.get_user_roles(user, project) + self.assertTrue('sysadmin' in project_roles) + self.assertTrue('netadmin' in project_roles) + # has role should be false user-level role is missing + self.assertFalse(self.manager.has_role(user, 'netadmin', project)) + + def test_can_remove_user_roles(self): + with user_and_project_generator(self.manager) as (user, project): + self.manager.add_role('test1', 'sysadmin') + self.assertTrue(user.has_role('sysadmin')) + self.manager.remove_role('test1', 'sysadmin') + self.assertFalse(user.has_role('sysadmin')) + + def test_removing_user_role_hides_it_from_project(self): + with user_and_project_generator(self.manager) as (user, project): + self.manager.add_role('test1', 'sysadmin') + project.add_role('test1', 'sysadmin') + self.assertTrue(project.has_role('test1', 'sysadmin')) + self.manager.remove_role('test1', 'sysadmin') + self.assertFalse(project.has_role('test1', 'sysadmin')) + + def test_can_remove_project_role_but_keep_user_role(self): + with user_and_project_generator(self.manager) as (user, project): + self.manager.add_role('test1', 'sysadmin') + project.add_role('test1', 'sysadmin') + self.assertTrue(user.has_role('sysadmin')) + self.assertTrue(project.has_role('test1', 'sysadmin')) + project.remove_role('test1', 'sysadmin') + self.assertFalse(project.has_role('test1', 'sysadmin')) + self.assertTrue(user.has_role('sysadmin')) + + def test_can_retrieve_project_by_user(self): + with user_and_project_generator(self.manager) as (user, project): + self.assertEqual(1, len(self.manager.get_projects('test1'))) + + def test_can_modify_project(self): + with user_and_project_generator(self.manager): + with user_generator(self.manager, name='test2'): + self.manager.modify_project('testproj', 'test2', 'new desc') + project = self.manager.get_project('testproj') + self.assertEqual('test2', project.project_manager_id) + self.assertEqual('new desc', project.description) + + def test_can_delete_project(self): + with user_generator(self.manager): + self.manager.create_project('testproj', 'test1') + self.assert_(self.manager.get_project('testproj')) + self.manager.delete_project('testproj') + projectlist = self.manager.get_projects() + self.assert_(not filter(lambda p: p.name == 'testproj', + projectlist)) + + def test_can_delete_user(self): + self.manager.create_user('test1') + self.assert_(self.manager.get_user('test1')) self.manager.delete_user('test1') - users = self.manager.get_users() - self.assertFalse(filter(lambda u: u.id == 'test1', users)) - self.manager.delete_user('test2') - self.assertEqual(self.manager.get_user('test2'), None) + userlist = self.manager.get_users() + self.assert_(not filter(lambda u: u.id == 'test1', userlist)) + + def test_can_modify_users(self): + with user_generator(self.manager): + self.manager.modify_user('test1', 'access', 'secret', True) + user = self.manager.get_user('test1') + self.assertEqual('access', user.access) + self.assertEqual('secret', user.secret) + self.assertTrue(user.is_admin()) if __name__ == "__main__": -- cgit From 39e06d83d265036eb0104cd55d4a828271f62e96 Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Sat, 25 Sep 2010 00:35:59 -0400 Subject: Test the AuthManager interface explicitly, in case the user/project wrappers fail or change at some point. Those interfaces should be tested on their own. --- nova/tests/auth_unittest.py | 113 +++++++++++++++++++++----------------------- 1 file changed, 55 insertions(+), 58 deletions(-) diff --git a/nova/tests/auth_unittest.py b/nova/tests/auth_unittest.py index 7065105c3..c3efc577f 100644 --- a/nova/tests/auth_unittest.py +++ b/nova/tests/auth_unittest.py @@ -75,11 +75,9 @@ class user_and_project_generator(object): self.manager.delete_user(self.user) self.manager.delete_project(self.project) -# TODO(todd): Have a test class that tests just the AuthManager functions -# and different test classes for User and Project wrappers -class AuthTestCase(test.BaseTestCase): +class AuthManagerTestCase(test.BaseTestCase): def setUp(self): - super(AuthTestCase, self).setUp() + super(AuthManagerTestCase, self).setUp() self.flags(connection_type='fake') self.manager = manager.AuthManager() @@ -151,54 +149,54 @@ class AuthTestCase(test.BaseTestCase): def test_can_create_project_with_manager(self): with user_and_project_generator(self.manager) as (user, project): self.assertEqual('test1', project.project_manager_id) - self.assertTrue(user.is_project_manager('testproj')) + self.assertTrue(self.manager.is_project_manager(user, project)) def test_create_project_assigns_manager_to_members(self): with user_and_project_generator(self.manager) as (user, project): - self.assertTrue(user.is_project_member('testproj')) - self.assertTrue(project.has_member('test1')) + self.assertTrue(self.manager.is_project_member(user, project)) def test_no_extra_project_members(self): with user_generator(self.manager, name='test2') as baduser: with user_and_project_generator(self.manager) as (user, project): - self.assertFalse(baduser.is_project_member('testproj')) - self.assertFalse(project.has_member('test2')) + self.assertFalse(self.manager.is_project_member(baduser, + project)) def test_no_extra_project_managers(self): with user_generator(self.manager, name='test2') as baduser: with user_and_project_generator(self.manager) as (user, project): - self.assertFalse(baduser.is_project_manager('testproj')) + self.assertFalse(self.manager.is_project_manager(baduser, + project)) def test_can_add_user_to_project(self): with user_generator(self.manager, name='test2') as user: - with user_and_project_generator(self.manager): - self.manager.add_to_project('test2', 'testproj') - self.assertTrue(user.is_project_member('testproj')) - # NOTE(todd): have to reload after the add_to_project + with user_and_project_generator(self.manager) as (_user, project): + self.manager.add_to_project(user, project) project = self.manager.get_project('testproj') - self.assertTrue(project.has_member('test2')) + self.assertTrue(self.manager.is_project_member(user, project)) def test_can_remove_user_from_project(self): with user_generator(self.manager, name='test2') as user: - with user_and_project_generator(self.manager): - self.manager.add_to_project('test2', 'testproj') - self.assertTrue(user.is_project_member('testproj')) - self.manager.remove_from_project('test2', 'testproj') - self.assertFalse(user.is_project_member('testproj')) - # NOTE(todd): have to reload after the membership modifications + with user_and_project_generator(self.manager) as (_user, project): + self.manager.add_to_project(user, project) project = self.manager.get_project('testproj') - self.assertFalse(project.has_member('test2')) + self.assertTrue(self.manager.is_project_member(user, project)) + self.manager.remove_from_project(user, project) + project = self.manager.get_project('testproj') + self.assertFalse(self.manager.is_project_member(user, project)) def test_can_add_remove_user_with_role(self): with user_generator(self.manager, name='test2') as user: - with user_and_project_generator(self.manager): - self.manager.add_to_project('test2', 'testproj') - self.manager.add_role('test2', 'developer', 'testproj') - self.assertTrue(user.is_project_member('testproj')) - self.manager.remove_from_project('test2', 'testproj') - self.assertFalse(self.manager.has_role('test2', 'developer', - 'testproj')) - self.assertFalse(user.is_project_member('testproj')) + with user_and_project_generator(self.manager) as (_user, project): + # NOTE(todd): after modifying users you must reload project + self.manager.add_to_project(user, project) + project = self.manager.get_project('testproj') + self.manager.add_role(user, 'developer', project) + self.assertTrue(self.manager.is_project_member(user, project)) + self.manager.remove_from_project(user, project) + project = self.manager.get_project('testproj') + self.assertFalse(self.manager.has_role(user, 'developer', + project)) + self.assertFalse(self.manager.is_project_member(user, project)) def test_can_generate_x509(self): # NOTE(todd): this doesn't assert against the auth manager @@ -231,31 +229,31 @@ class AuthTestCase(test.BaseTestCase): def test_adding_role_to_project_is_ignored_unless_added_to_user(self): with user_and_project_generator(self.manager) as (user, project): - self.assertFalse(project.has_role('test1', 'sysadmin')) - project.add_role('test1', 'sysadmin') + self.assertFalse(self.manager.has_role(user, 'sysadmin', project)) + self.manager.add_role(user, 'sysadmin', project) # NOTE(todd): it will still show up in get_user_roles(u, project) - self.assertFalse(project.has_role('test1', 'sysadmin')) - self.manager.add_role('test1', 'sysadmin') - self.assertTrue(project.has_role('test1', 'sysadmin')) + self.assertFalse(self.manager.has_role(user, 'sysadmin', project)) + self.manager.add_role(user, 'sysadmin') + self.assertTrue(self.manager.has_role(user, 'sysadmin', project)) def test_add_user_role_doesnt_infect_project_roles(self): with user_and_project_generator(self.manager) as (user, project): - self.assertFalse(project.has_role('test1', 'sysadmin')) - self.manager.add_role('test1', 'sysadmin') - self.assertFalse(project.has_role('test1', 'sysadmin')) + self.assertFalse(self.manager.has_role(user, 'sysadmin', project)) + self.manager.add_role(user, 'sysadmin') + self.assertFalse(self.manager.has_role(user, 'sysadmin', project)) def test_can_list_user_roles(self): with user_and_project_generator(self.manager) as (user, project): - self.manager.add_role('test1', 'sysadmin') + self.manager.add_role(user, 'sysadmin') roles = self.manager.get_user_roles(user) self.assertTrue('sysadmin' in roles) self.assertFalse('netadmin' in roles) def test_can_list_project_roles(self): with user_and_project_generator(self.manager) as (user, project): - self.manager.add_role('test1', 'sysadmin') - project.add_role('test1', 'sysadmin') - project.add_role('test1', 'netadmin') + self.manager.add_role(user, 'sysadmin') + self.manager.add_role(user, 'sysadmin', project) + self.manager.add_role(user, 'netadmin', project) project_roles = self.manager.get_user_roles(user, project) self.assertTrue('sysadmin' in project_roles) self.assertTrue('netadmin' in project_roles) @@ -264,28 +262,27 @@ class AuthTestCase(test.BaseTestCase): def test_can_remove_user_roles(self): with user_and_project_generator(self.manager) as (user, project): - self.manager.add_role('test1', 'sysadmin') - self.assertTrue(user.has_role('sysadmin')) - self.manager.remove_role('test1', 'sysadmin') - self.assertFalse(user.has_role('sysadmin')) + self.manager.add_role(user, 'sysadmin') + self.assertTrue(self.manager.has_role(user, 'sysadmin')) + self.manager.remove_role(user, 'sysadmin') + self.assertFalse(self.manager.has_role(user, 'sysadmin')) def test_removing_user_role_hides_it_from_project(self): with user_and_project_generator(self.manager) as (user, project): - self.manager.add_role('test1', 'sysadmin') - project.add_role('test1', 'sysadmin') - self.assertTrue(project.has_role('test1', 'sysadmin')) - self.manager.remove_role('test1', 'sysadmin') - self.assertFalse(project.has_role('test1', 'sysadmin')) + self.manager.add_role(user, 'sysadmin') + self.manager.add_role(user, 'sysadmin', project) + self.assertTrue(self.manager.has_role(user, 'sysadmin', project)) + self.manager.remove_role(user, 'sysadmin') + self.assertFalse(self.manager.has_role(user, 'sysadmin', project)) def test_can_remove_project_role_but_keep_user_role(self): with user_and_project_generator(self.manager) as (user, project): - self.manager.add_role('test1', 'sysadmin') - project.add_role('test1', 'sysadmin') - self.assertTrue(user.has_role('sysadmin')) - self.assertTrue(project.has_role('test1', 'sysadmin')) - project.remove_role('test1', 'sysadmin') - self.assertFalse(project.has_role('test1', 'sysadmin')) - self.assertTrue(user.has_role('sysadmin')) + self.manager.add_role(user, 'sysadmin') + self.manager.add_role(user, 'sysadmin', project) + self.assertTrue(self.manager.has_role(user, 'sysadmin')) + self.manager.remove_role(user, 'sysadmin', project) + self.assertFalse(self.manager.has_role(user, 'sysadmin', project)) + self.assertTrue(self.manager.has_role(user, 'sysadmin')) def test_can_retrieve_project_by_user(self): with user_and_project_generator(self.manager) as (user, project): -- cgit From c3fcb1b2176f4b7afbffb3555da55c0754bacaad Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Sat, 25 Sep 2010 01:05:39 -0700 Subject: flush the nova chains --- tools/setup_iptables.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/setup_iptables.sh b/tools/setup_iptables.sh index 7368fadf9..d045b50cd 100755 --- a/tools/setup_iptables.sh +++ b/tools/setup_iptables.sh @@ -48,6 +48,9 @@ if [ "$CMD" == "clear" ]; then iptables -P FORWARD ACCEPT iptables -P OUTPUT ACCEPT iptables -F + iptables -F nova_input + iptables -F nova_output + iptables -F nova_forward iptables -X fi -- cgit From 125e69dd42f6f91f727258dc388d15ce63076d1f Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Sat, 25 Sep 2010 02:51:50 -0700 Subject: allow mgmt ip access to api --- tools/setup_iptables.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/setup_iptables.sh b/tools/setup_iptables.sh index d045b50cd..b7e2f9a11 100755 --- a/tools/setup_iptables.sh +++ b/tools/setup_iptables.sh @@ -106,6 +106,9 @@ fi if [ "$CMD" == "api" ] || [ "$CMD" == "all" ]; then iptables -A nova_input -m tcp -p tcp -d $IP --dport 8773 -j ACCEPT + if [ "$IP" != "$MGMT_IP" ]; then + iptables -A nova_input -m tcp -p tcp -d $MGMT_IP --dport 8773 -j ACCEPT + fi fi if [ "$CMD" == "redis" ] || [ "$CMD" == "all" ]; then -- cgit From 7ce67ea60f6e7d20665c10318b29e2659fd91513 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Sat, 25 Sep 2010 08:35:16 -0700 Subject: fix a few missed calls to _confirm_rule and 80 char issues --- nova/network/linux_net.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index 38a616e83..8058c970d 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -60,8 +60,10 @@ def init_host(): "-j SNAT --to-source %s" % (FLAGS.private_range, FLAGS.routing_source_ip)) - _confirm_rule("POSTROUTING", "-t nat -s %s -j MASQUERADE" % FLAGS.private_range) - _confirm_rule("POSTROUTING", "-t nat -s %(range)s -d %(range)s -j ACCEPT" % {'range': FLAGS.private_range}) + _confirm_rule("POSTROUTING", "-t nat -s %s -j MASQUERADE" % + FLAGS.private_range) + _confirm_rule("POSTROUTING", "-t nat -s %(range)s -d %(range)s -j ACCEPT" % + {'range': FLAGS.private_range}) def bind_floating_ip(floating_ip): """Bind ip to public interface""" @@ -77,9 +79,10 @@ def unbind_floating_ip(floating_ip): def ensure_vlan_forward(public_ip, port, private_ip): """Sets up forwarding rules for vlan""" - _confirm_rule("FORWARD", "-d %s -p udp --dport 1194 -j ACCEPT" % private_ip) - _confirm_rule( - "PREROUTING -t nat -d %s -p udp --dport %s -j DNAT --to %s:1194" + _confirm_rule("FORWARD", "-d %s -p udp --dport 1194 -j ACCEPT" % + private_ip) + _confirm_rule("PREROUTING", + "-t nat -d %s -p udp --dport %s -j DNAT --to %s:1194" % (public_ip, port, private_ip)) @@ -93,8 +96,7 @@ def ensure_floating_forward(floating_ip, fixed_ip): _confirm_rule("FORWARD", "-d %s -p icmp -j ACCEPT" % (fixed_ip)) for (protocol, port) in DEFAULT_PORTS: - _confirm_rule( - "FORWARD -d %s -p %s --dport %s -j ACCEPT" + _confirm_rule("FORWARD","-d %s -p %s --dport %s -j ACCEPT" % (fixed_ip, protocol, port)) -- cgit From 888a99182ca3152f68b762dab4fc95d7d3f1cadb Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Sat, 25 Sep 2010 09:28:05 -0700 Subject: add forwarding ACCEPT for outgoing packets on compute host --- nova/network/linux_net.py | 1 + 1 file changed, 1 insertion(+) diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index 8058c970d..78cc64cb0 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -148,6 +148,7 @@ def ensure_bridge(bridge, interface, net_attrs=None): _confirm_rule("FORWARD", "--in-interface %s -j ACCEPT" % bridge) else: _execute("sudo ifconfig %s up" % bridge) + _confirm_rule("FORWARD", "--out-interface %s -j ACCEPT" % bridge) def get_dhcp_hosts(context, network_id): -- cgit From 6a3cd55a9c933c329da1117179d676e9141c5b4d Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Sat, 25 Sep 2010 10:47:51 -0700 Subject: disable output drop for the moment because it is too restrictive --- tools/setup_iptables.sh | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tools/setup_iptables.sh b/tools/setup_iptables.sh index b7e2f9a11..dd91c76e0 100755 --- a/tools/setup_iptables.sh +++ b/tools/setup_iptables.sh @@ -52,6 +52,11 @@ if [ "$CMD" == "clear" ]; then iptables -F nova_output iptables -F nova_forward iptables -X + iptables -t nat -F + iptables -t nat -F nova_input + iptables -t nat -F nova_output + iptables -t nat -F nova_forward + iptables -t nat -X fi if [ "$CMD" == "base" ] || [ "$CMD" == "all" ]; then @@ -73,7 +78,7 @@ if [ "$CMD" == "base" ] || [ "$CMD" == "all" ]; then iptables -N nova_forward iptables -A FORWARD -j nova_forward - iptables -P OUTPUT DROP + # iptables -P OUTPUT DROP # too restrictive for the moment iptables -A OUTPUT -m state --state INVALID -j DROP iptables -A OUTPUT -m state --state RELATED,ESTABLISHED -j ACCEPT iptables -N nova_output -- cgit From 0d0884b2c1692d03e0994baecbb23ce24ef71e44 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Sat, 25 Sep 2010 13:53:29 -0700 Subject: allow in and out for network and compute hosts --- 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 78cc64cb0..6b47b6d97 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -145,10 +145,10 @@ def ensure_bridge(bridge, interface, net_attrs=None): net_attrs['gateway'], net_attrs['broadcast'], net_attrs['netmask'])) - _confirm_rule("FORWARD", "--in-interface %s -j ACCEPT" % bridge) else: _execute("sudo ifconfig %s up" % bridge) - _confirm_rule("FORWARD", "--out-interface %s -j ACCEPT" % bridge) + _confirm_rule("FORWARD", "--in-interface %s -j ACCEPT" % bridge) + _confirm_rule("FORWARD", "--out-interface %s -j ACCEPT" % bridge) def get_dhcp_hosts(context, network_id): -- cgit From 5d6ab2b2540743e0a53b01129df722610b3ae3b6 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Sat, 25 Sep 2010 18:33:27 -0700 Subject: reorganize iptables clear and make sure use_nova_chains is a boolean --- nova/network/linux_net.py | 4 ++-- tools/setup_iptables.sh | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index 6b47b6d97..01a1d2ad0 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -41,8 +41,8 @@ flags.DEFINE_string('bridge_dev', 'eth0', 'network device for bridges') flags.DEFINE_string('routing_source_ip', utils.get_my_ip(), 'Public IP of network host') -flags.DEFINE_string('use_nova_chains', False, - 'use the nova_ routing chains instead of default') +flags.DEFINE_boo('use_nova_chains', False, + 'use the nova_ routing chains instead of default') DEFAULT_PORTS = [("tcp", 80), ("tcp", 22), ("udp", 1194), ("tcp", 443)] diff --git a/tools/setup_iptables.sh b/tools/setup_iptables.sh index dd91c76e0..b6b8414e3 100755 --- a/tools/setup_iptables.sh +++ b/tools/setup_iptables.sh @@ -48,15 +48,15 @@ if [ "$CMD" == "clear" ]; then iptables -P FORWARD ACCEPT iptables -P OUTPUT ACCEPT iptables -F + iptables -t nat -F iptables -F nova_input iptables -F nova_output iptables -F nova_forward - iptables -X - iptables -t nat -F iptables -t nat -F nova_input iptables -t nat -F nova_output iptables -t nat -F nova_forward iptables -t nat -X + iptables -X fi if [ "$CMD" == "base" ] || [ "$CMD" == "all" ]; then -- cgit From 4b26e60e3c84d4535fbd4ba7a3b2bf29f2121072 Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Tue, 28 Sep 2010 14:09:53 -0400 Subject: Instance & Image renaming fixes. --- nova/endpoint/cloud.py | 24 ++++++++++-------------- nova/endpoint/images.py | 6 ++++-- nova/objectstore/handler.py | 18 +++++++++--------- nova/objectstore/image.py | 14 ++++++-------- nova/tests/cloud_unittest.py | 2 +- nova/tests/objectstore_unittest.py | 6 +++--- 6 files changed, 33 insertions(+), 37 deletions(-) diff --git a/nova/endpoint/cloud.py b/nova/endpoint/cloud.py index 2c174bb81..9c96ced9c 100644 --- a/nova/endpoint/cloud.py +++ b/nova/endpoint/cloud.py @@ -310,10 +310,11 @@ class CloudController(object): 'volume_id': volume['str_id']}] else: v['attachmentSet'] = [{}] - if 'display_name' in volume: - v['display_name'] = volume['display_name'] - if 'display_description' in volume: - v['display_description'] = volume['display_description'] + + # TODO(todd): check api version and only pass back to nova-aware + # clients + v['display_name'] = volume['display_name'] + v['display_description'] = volume['display_description'] return v @rbac.allow('projectmanager', 'sysadmin') @@ -746,7 +747,9 @@ class CloudController(object): if field in kwargs: changes[field] = kwargs[field] if changes: - db.instance_update(context, instance_id, kwargs) + db_context = {} + inst = db.instance_get_by_str(db_context, instance_id) + db.instance_update(db_context, inst['id'], kwargs) return defer.succeed(True) @rbac.allow('projectmanager', 'sysadmin') @@ -814,13 +817,6 @@ class CloudController(object): return defer.succeed(result) @rbac.allow('projectmanager', 'sysadmin') - def set_image_name(self, context, image_id, name): - result = images.update_user_editable_field(context, image_id, - 'displayName', name) - return defer.succeed(result) - - @rbac.allow('projectmanager', 'sysadmin') - def set_image_description(self, context, image_id, name): - result = images.update_user_editable_field(context, image_id, - 'displayDescription', name) + def update_image(self, context, image_id, **kwargs): + result = images.update(context, image_id, dict(kwargs)) return defer.succeed(result) diff --git a/nova/endpoint/images.py b/nova/endpoint/images.py index a63c059bb..cb54cdda2 100644 --- a/nova/endpoint/images.py +++ b/nova/endpoint/images.py @@ -43,11 +43,13 @@ def modify(context, image_id, operation): return True -def update_user_editable_field(context, image_id, field, value): +def update(context, image_id, attributes): + """update an image's attributes / info.json""" + attributes.update({"image_id": image_id}) conn(context).make_request( method='POST', bucket='_images', - query_args=qs({'image_id': image_id, 'field': field, 'value': value})) + query_args=qs(attributes)) return True def register(context, image_location): diff --git a/nova/objectstore/handler.py b/nova/objectstore/handler.py index f4596e982..daf633ac2 100644 --- a/nova/objectstore/handler.py +++ b/nova/objectstore/handler.py @@ -352,6 +352,8 @@ class ImagesResource(resource.Resource): m[u'imageType'] = m['type'] elif 'imageType' in m: m[u'type'] = m['imageType'] + if 'displayName' not in m: + m[u'displayName'] = u'' return m request.write(json.dumps([decorate(i.metadata) for i in images])) @@ -386,23 +388,21 @@ class ImagesResource(resource.Resource): image_id = get_argument(request, 'image_id', u'') image_object = image.Image(image_id) if not image_object.is_authorized(request.context): - logging.debug("not authorized for handle_POST in images") + logging.debug("not authorized for render_POST in images") raise exception.NotAuthorized operation = get_argument(request, 'operation', u'') - field = get_argument(request, 'field', u'') - value = get_argument(request, 'value', u'') if operation: # operation implies publicity toggle logging.debug("handling publicity toggle") image_object.set_public(operation=='add') - elif field: - # field implies user field editing (value can be blank) - logging.debug("update user field") - image_object.update_user_editable_field(field, value) else: - logging.debug("unknown action for handle_POST in images") - + # other attributes imply update + logging.debug("update user fields") + clean_args = {} + for arg in request.args.keys(): + clean_args[arg] = request.args[arg][0] + image_object.update_user_editable_fields(clean_args) return '' def render_DELETE(self, request): # pylint: disable-msg=R0201 diff --git a/nova/objectstore/image.py b/nova/objectstore/image.py index ad1745af6..def1b8167 100644 --- a/nova/objectstore/image.py +++ b/nova/objectstore/image.py @@ -82,15 +82,13 @@ class Image(object): with open(os.path.join(self.path, 'info.json'), 'w') as f: json.dump(md, f) - def update_user_editable_field(self, field, value): - fields = ['displayName', 'displayDescription'] - if field not in fields: - raise KeyError("Invalid field: %s" % field) + def update_user_editable_fields(self, args): + """args is from the request parameters, so requires extra cleaning""" + fields = {'display_name': 'displayName', 'description': 'description'} info = self.metadata - if value: - info[field] = value - elif field in info: - del info[field] + for field in fields.keys(): + if field in args: + info[fields[field]] = args[field] with open(os.path.join(self.path, 'info.json'), 'w') as f: json.dump(info, f) diff --git a/nova/tests/cloud_unittest.py b/nova/tests/cloud_unittest.py index cea2a67ce..f7340f12d 100644 --- a/nova/tests/cloud_unittest.py +++ b/nova/tests/cloud_unittest.py @@ -219,7 +219,7 @@ class CloudTestCase(test.BaseTestCase): def test_update_of_instance_display_fields(self): inst = db.instance_create({}, {}) - self.cloud.update_instance(self.context, inst['id'], + self.cloud.update_instance(self.context, inst['str_id'], display_name='c00l 1m4g3') inst = db.instance_get({}, inst['id']) self.assertEqual('c00l 1m4g3', inst['display_name']) diff --git a/nova/tests/objectstore_unittest.py b/nova/tests/objectstore_unittest.py index 82c6e2c49..4216cf190 100644 --- a/nova/tests/objectstore_unittest.py +++ b/nova/tests/objectstore_unittest.py @@ -165,10 +165,10 @@ class ObjectStoreTestCase(test.BaseTestCase): self.assertFalse(my_img.is_authorized(self.context)) # change user-editable fields - my_img.update_user_editable_field('displayName', 'my cool image') + my_img.update_user_editable_fields({'display_name': 'my cool image'}) self.assertEqual('my cool image', my_img.metadata['displayName']) - my_img.update_user_editable_field('displayName', '') - self.assert_(not 'displayName' in my_img.metadata) + my_img.update_user_editable_fields({'display_name': ''}) + self.assert_(not my_img.metadata['displayName']) class TestHTTPChannel(http.HTTPChannel): -- cgit From 08a6dc59add5d72c6d925d1ca43868557a4b3148 Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Tue, 28 Sep 2010 15:54:05 -0400 Subject: Hook the AuthManger#modify_user method into nova-manage commands. --- bin/nova-manage | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/bin/nova-manage b/bin/nova-manage index baa1cb4db..da18e7d1b 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -228,6 +228,19 @@ class UserCommands(object): for user in self.manager.get_users(): print user.name + def modify(self, name, access_key, secret_key, is_admin): + """update a users keys & admin flag + arguments: accesskey secretkey admin + leave any field blank to ignore it, admin should be 'T', 'F', or blank + """ + if not is_admin: + is_admin = None + elif is_admin.upper()[0] == 'T': + is_admin = True + else: + is_admin = False + print "is_admin: %r" % is_admin + self.manager.modify_user(name, access_key, secret_key, is_admin) class ProjectCommands(object): """Class for managing projects.""" -- cgit From c1f7914f9d8c4f7687c67de37c5eda5a95245a0d Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Tue, 28 Sep 2010 16:41:39 -0400 Subject: Remove TODO, since apparently newer boto doesn't die on extra fields. --- nova/api/ec2/cloud.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 4defef87e..528380f0f 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -286,8 +286,6 @@ class CloudController(object): else: v['attachmentSet'] = [{}] - # TODO(todd): check api version and only pass back to nova-aware - # clients v['display_name'] = volume['display_name'] v['display_description'] = volume['display_description'] return v -- cgit From c80c0786baadf521c86ceff21288e3760aaea5bd Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Tue, 28 Sep 2010 16:47:29 -0400 Subject: Add authorization info for cloud endpoints. --- nova/api/ec2/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/nova/api/ec2/__init__.py b/nova/api/ec2/__init__.py index f0aa57ee4..7a958f841 100644 --- a/nova/api/ec2/__init__.py +++ b/nova/api/ec2/__init__.py @@ -158,12 +158,14 @@ class Authorizer(wsgi.Middleware): 'RunInstances': ['projectmanager', 'sysadmin'], 'TerminateInstances': ['projectmanager', 'sysadmin'], 'RebootInstances': ['projectmanager', 'sysadmin'], + 'UpdateInstance': ['projectmanager', 'sysadmin'], 'DeleteVolume': ['projectmanager', 'sysadmin'], 'DescribeImages': ['all'], 'DeregisterImage': ['projectmanager', 'sysadmin'], 'RegisterImage': ['projectmanager', 'sysadmin'], 'DescribeImageAttribute': ['all'], 'ModifyImageAttribute': ['projectmanager', 'sysadmin'], + 'UpdateImage': ['projectmanager', 'sysadmin'], }, 'AdminController': { # All actions have the same permission: ['none'] (the default) -- cgit From c4df3d63c83c073664a9ed0abaefe2adbe4cd061 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Tue, 28 Sep 2010 14:59:32 -0700 Subject: fix test for editable image --- nova/tests/cloud_unittest.py | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/nova/tests/cloud_unittest.py b/nova/tests/cloud_unittest.py index 8db4aeb77..cce4a5568 100644 --- a/nova/tests/cloud_unittest.py +++ b/nova/tests/cloud_unittest.py @@ -202,18 +202,37 @@ class CloudTestCase(test.BaseTestCase): # data = self.cloud.get_metadata(instance(i)['private_dns_name']) # self.assert_(data['meta-data']['ami-id'] == 'ami-%s' % i) + @staticmethod + def _fake_set_image_description(ctxt, image_id, description): + from nova.objectstore import handler + class req: + pass + request = req() + request.context = ctxt + request.args = {'image_id': [image_id], + 'description': [description]} + + resource = handler.ImagesResource() + resource.render_POST(request) + def test_user_editable_image_endpoint(self): pathdir = os.path.join(FLAGS.images_path, 'ami-testing') os.mkdir(pathdir) - info = {} + info = {'isPublic': False} with open(os.path.join(pathdir, 'info.json'), 'w') as f: json.dump(info, f) - yield self.cloud.set_image_description(self.context, 'ami-testing', - 'Foo Img') img = image.Image('ami-testing') - self.assertEqual('Foo Img', img.metadata['displayDescription']) - self.cloud.set_image_description(self.context, 'ami-testing', '') - self.assert_(not 'displayDescription' in img.metadata) + # self.cloud.set_image_description(self.context, 'ami-testing', + # 'Foo Img') + # NOTE(vish): Above won't work unless we start objectstore or create + # a fake version of api/ec2/images.py conn that can + # call methods directly instead of going through boto. + # for now, just cheat and call the method directly + self._fake_set_image_description(self.context, 'ami-testing', + 'Foo Img') + self.assertEqual('Foo Img', img.metadata['description']) + self._fake_set_image_description(self.context, 'ami-testing', '') + self.assertEqual('', img.metadata['description']) def test_update_of_instance_display_fields(self): inst = db.instance_create({}, {}) -- cgit From 1e4bca12e7e06698d3a13d6a208be90647f27555 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Tue, 28 Sep 2010 17:15:59 -0700 Subject: typo s/boo/bool --- 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 01a1d2ad0..6f1d594fa 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -41,8 +41,8 @@ flags.DEFINE_string('bridge_dev', 'eth0', 'network device for bridges') flags.DEFINE_string('routing_source_ip', utils.get_my_ip(), 'Public IP of network host') -flags.DEFINE_boo('use_nova_chains', False, - 'use the nova_ routing chains instead of default') +flags.DEFINE_bool('use_nova_chains', False, + 'use the nova_ routing chains instead of default') DEFAULT_PORTS = [("tcp", 80), ("tcp", 22), ("udp", 1194), ("tcp", 443)] -- cgit From 533f72379931aa7bf67a0e7d1d7664ca151afda0 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Tue, 28 Sep 2010 17:24:25 -0700 Subject: fix flag defaults --- nova/flags.py | 2 +- nova/network/linux_net.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/nova/flags.py b/nova/flags.py index 5ed0f92ee..92f6766cf 100644 --- a/nova/flags.py +++ b/nova/flags.py @@ -190,7 +190,7 @@ DEFINE_string('rabbit_virtual_host', '/', 'rabbit virtual host') DEFINE_string('control_exchange', 'nova', 'the main exchange to connect to') DEFINE_string('cc_host', '127.0.0.1', 'ip of api server') DEFINE_integer('cc_port', 8773, 'cloud controller port') -DEFINE_string('ec2_url', 'http://%s:%s/services/Cloud' % (FLAGS.cc_host, FLAGS.cc_port), +DEFINE_string('ec2_url', 'http://127.0.0.1:8773/services/Cloud' 'Url to ec2 api server') DEFINE_string('default_image', 'ami-11111', diff --git a/nova/network/linux_net.py b/nova/network/linux_net.py index 6f1d594fa..fa77c5ba8 100644 --- a/nova/network/linux_net.py +++ b/nova/network/linux_net.py @@ -39,7 +39,7 @@ flags.DEFINE_string('public_interface', 'vlan1', 'Interface for public IP addresses') flags.DEFINE_string('bridge_dev', 'eth0', 'network device for bridges') -flags.DEFINE_string('routing_source_ip', utils.get_my_ip(), +flags.DEFINE_string('routing_source_ip', '127.0.0.1', 'Public IP of network host') flags.DEFINE_bool('use_nova_chains', False, 'use the nova_ routing chains instead of default') -- cgit From 05e3e188e03624884ed019fe9cd8f216c9262f98 Mon Sep 17 00:00:00 2001 From: Michael Gundlach Date: Tue, 28 Sep 2010 20:36:50 -0400 Subject: Fault support --- nova/api/rackspace/__init__.py | 9 +++-- nova/api/rackspace/auth.py | 7 ++-- nova/api/rackspace/faults.py | 61 ++++++++++++++++++++++++++++++++++ nova/api/rackspace/flavors.py | 3 +- nova/api/rackspace/images.py | 7 ++-- nova/tests/api/rackspace/testfaults.py | 30 +++++++++++++++++ 6 files changed, 107 insertions(+), 10 deletions(-) create mode 100644 nova/api/rackspace/faults.py create mode 100644 nova/tests/api/rackspace/testfaults.py diff --git a/nova/api/rackspace/__init__.py b/nova/api/rackspace/__init__.py index c24d08585..447037020 100644 --- a/nova/api/rackspace/__init__.py +++ b/nova/api/rackspace/__init__.py @@ -31,6 +31,7 @@ import webob from nova import flags from nova import utils from nova import wsgi +from nova.api.rackspace import faults from nova.api.rackspace import flavors from nova.api.rackspace import images from nova.api.rackspace import ratelimiting @@ -66,7 +67,7 @@ class AuthMiddleware(wsgi.Middleware): user = self.auth_driver.authorize_token(req.headers["X-Auth-Token"]) if not user: - return webob.exc.HTTPUnauthorized() + return faults.Fault(webob.exc.HTTPUnauthorized()) context = {'user': user} req.environ['nova.context'] = context return self.application @@ -109,8 +110,10 @@ class RateLimitingMiddleware(wsgi.Middleware): delay = self.get_delay(action_name, username) if delay: # TODO(gundlach): Get the retry-after format correct. - raise webob.exc.HTTPRequestEntityTooLarge(headers={ - 'Retry-After': time.time() + delay}) + exc = webob.exc.HTTPRequestEntityTooLarge( + explanation='Too many requests.', + headers={'Retry-After': time.time() + delay}) + raise faults.Fault(exc) return self.application def get_delay(self, action_name, username): diff --git a/nova/api/rackspace/auth.py b/nova/api/rackspace/auth.py index ce5a967eb..519263367 100644 --- a/nova/api/rackspace/auth.py +++ b/nova/api/rackspace/auth.py @@ -9,6 +9,7 @@ from nova import auth from nova import manager from nova import db from nova import utils +from nova.api.rackspace import faults FLAGS = flags.FLAGS @@ -34,13 +35,13 @@ class BasicApiAuthManager(object): # honor it path_info = req.path_info if len(path_info) > 1: - return webob.exc.HTTPUnauthorized() + return faults.Fault(webob.exc.HTTPUnauthorized()) try: username, key = req.headers['X-Auth-User'], \ req.headers['X-Auth-Key'] except KeyError: - return webob.exc.HTTPUnauthorized() + return faults.Fault(webob.exc.HTTPUnauthorized()) username, key = req.headers['X-Auth-User'], req.headers['X-Auth-Key'] token, user = self._authorize_user(username, key) @@ -55,7 +56,7 @@ class BasicApiAuthManager(object): res.status = '204' return res else: - return webob.exc.HTTPUnauthorized() + return faults.Fault(webob.exc.HTTPUnauthorized()) def authorize_token(self, token_hash): """ retrieves user information from the datastore given a token diff --git a/nova/api/rackspace/faults.py b/nova/api/rackspace/faults.py new file mode 100644 index 000000000..fd6bc3623 --- /dev/null +++ b/nova/api/rackspace/faults.py @@ -0,0 +1,61 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2010 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 webob.dec + +from nova import wsgi + + +class Fault(wsgi.Application): + + """An RS API fault response.""" + + _fault_names = { + 400: "badRequest", + 401: "unauthorized", + 403: "resizeNotAllowed", + 404: "itemNotFound", + 405: "badMethod", + 409: "inProgress", + 413: "overLimit", + 415: "badMediaType", + 501: "notImplemented", + 503: "serviceUnavailable"} + + def __init__(self, exception): + """Create a Fault for the given webob.exc.exception.""" + self.exception = exception + + @webob.dec.wsgify + def __call__(self, req): + """Generate a WSGI response based on self.exception.""" + # Replace the body with fault details. + code = self.exception.status_int + fault_name = self._fault_names.get(code, "cloudServersFault") + fault_data = { + fault_name: { + 'code': code, + 'message': self.exception.explanation}} + if code == 413: + retry = self.exception.headers['Retry-After'] + fault_data[fault_name]['retryAfter'] = retry + # 'code' is an attribute on the fault tag itself + metadata = {'application/xml': {'attributes': {fault_name: 'code'}}} + serializer = wsgi.Serializer(req.environ, metadata) + self.exception.body = serializer.to_content_type(fault_data) + return self.exception diff --git a/nova/api/rackspace/flavors.py b/nova/api/rackspace/flavors.py index 60b35c939..6cc57be33 100644 --- a/nova/api/rackspace/flavors.py +++ b/nova/api/rackspace/flavors.py @@ -16,6 +16,7 @@ # under the License. from nova.api.rackspace import base +from nova.api.rackspace import faults from nova.compute import instance_types from webob import exc @@ -47,7 +48,7 @@ class Controller(base.Controller): item = dict(ram=val['memory_mb'], disk=val['local_gb'], id=val['flavorid'], name=name) return dict(flavor=item) - raise exc.HTTPNotFound() + raise faults.Fault(exc.HTTPNotFound()) def _all_ids(self): """Return the list of all flavorids.""" diff --git a/nova/api/rackspace/images.py b/nova/api/rackspace/images.py index 2f3e928b9..1c50d0bec 100644 --- a/nova/api/rackspace/images.py +++ b/nova/api/rackspace/images.py @@ -18,6 +18,7 @@ import nova.image.service from nova.api.rackspace import base from nova.api.rackspace import _id_translator +from nova.api.rackspace import faults from webob import exc class Controller(base.Controller): @@ -57,14 +58,14 @@ class Controller(base.Controller): def delete(self, req, id): # Only public images are supported for now. - raise exc.HTTPNotFound() + raise faults.Fault(exc.HTTPNotFound()) def create(self, req): # Only public images are supported for now, so a request to # make a backup of a server cannot be supproted. - raise exc.HTTPNotFound() + raise faults.Fault(exc.HTTPNotFound()) def update(self, req, id): # Users may not modify public images, and that's all that # we support for now. - raise exc.HTTPNotFound() + raise faults.Fault(exc.HTTPNotFound()) diff --git a/nova/tests/api/rackspace/testfaults.py b/nova/tests/api/rackspace/testfaults.py new file mode 100644 index 000000000..74caffd30 --- /dev/null +++ b/nova/tests/api/rackspace/testfaults.py @@ -0,0 +1,30 @@ +import unittest +import webob +import webob.exc + +from nova.api.rackspace import faults + +class TestFaults(unittest.TestCase): + + def test_fault_parts(self): + req = webob.Request.blank('/.xml') + f = faults.Fault(webob.exc.HTTPBadRequest(explanation='scram')) + resp = req.get_response(f) + + first_two_words = resp.body.strip().split()[:2] + self.assertEqual(first_two_words, ['']) + body_without_spaces = ''.join(resp.body.split()) + self.assertTrue('scram' in body_without_spaces) + + def test_retry_header(self): + req = webob.Request.blank('/.xml') + exc = webob.exc.HTTPRequestEntityTooLarge(explanation='sorry', + headers={'Retry-After': 4}) + f = faults.Fault(exc) + resp = req.get_response(f) + first_two_words = resp.body.strip().split()[:2] + self.assertEqual(first_two_words, ['']) + body_sans_spaces = ''.join(resp.body.split()) + self.assertTrue('sorry' in body_sans_spaces) + self.assertTrue('4' in body_sans_spaces) + self.assertEqual(resp.headers['Retry-After'], 4) -- cgit From d1c454ba4331794931e94cc2864f4e1a6ef5bf22 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Tue, 28 Sep 2010 17:41:57 -0700 Subject: improved commenting --- tools/setup_iptables.sh | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/tools/setup_iptables.sh b/tools/setup_iptables.sh index b6b8414e3..673353eb4 100755 --- a/tools/setup_iptables.sh +++ b/tools/setup_iptables.sh @@ -17,6 +17,13 @@ # License for the specific language governing permissions and limitations # under the License. +# NOTE(vish): This script sets up some reasonable defaults for iptables and +# creates nova-specific chains. If you use this script you should +# run nova-network and nova-compute with --use_nova_chains=True + +# NOTE(vish): If you run nova-api on a different port, make sure to change +# the port here +API_PORT=${API_PORT:-"8773"} if [ -n "$1" ]; then CMD=$1 else @@ -26,8 +33,9 @@ fi if [ -n "$2" ]; then IP=$2 else - # NOTE(vish): this will just get the first ip in the list, so if you - # have more than one eth device set up, this will fail + # NOTE(vish): This will just get the first ip in the list, so if you + # have more than one eth device set up, this will fail, and + # you should explicitly pass in the ip of the instance IP=`ifconfig | grep -m 1 'inet addr:'| cut -d: -f2 | awk '{print $1}'` fi @@ -39,6 +47,8 @@ fi if [ -n "$4" ]; then + # NOTE(vish): Management IP is the ip over which to allow ssh traffic. It + # will also allow traffic to nova-api MGMT_IP=$4 else MGMT_IP="$IP" @@ -78,7 +88,9 @@ if [ "$CMD" == "base" ] || [ "$CMD" == "all" ]; then iptables -N nova_forward iptables -A FORWARD -j nova_forward - # iptables -P OUTPUT DROP # too restrictive for the moment + # NOTE(vish): DROP on output is too restrictive for now. We need to add + # in a bunch of more specific output rules to use it. + # iptables -P OUTPUT DROP iptables -A OUTPUT -m state --state INVALID -j DROP iptables -A OUTPUT -m state --state RELATED,ESTABLISHED -j ACCEPT iptables -N nova_output @@ -99,8 +111,9 @@ if [ "$CMD" == "ganglia" ] || [ "$CMD" == "all" ]; then iptables -A nova_input -m udp -p udp -d $IP --dport 8649 -j ACCEPT fi -if [ "$CMD" == "dashboard" ] || [ "$CMD" == "all" ]; then - # dashboard +if [ "$CMD" == "web" ] || [ "$CMD" == "all" ]; then + # NOTE(vish): This opens up ports for web access, allowing web-based + # dashboards to work. iptables -A nova_input -m tcp -p tcp -d $IP --dport 80 -j ACCEPT iptables -A nova_input -m tcp -p tcp -d $IP --dport 443 -j ACCEPT fi @@ -110,9 +123,9 @@ if [ "$CMD" == "objectstore" ] || [ "$CMD" == "all" ]; then fi if [ "$CMD" == "api" ] || [ "$CMD" == "all" ]; then - iptables -A nova_input -m tcp -p tcp -d $IP --dport 8773 -j ACCEPT + iptables -A nova_input -m tcp -p tcp -d $IP --dport $API_PORT -j ACCEPT if [ "$IP" != "$MGMT_IP" ]; then - iptables -A nova_input -m tcp -p tcp -d $MGMT_IP --dport 8773 -j ACCEPT + iptables -A nova_input -m tcp -p tcp -d $MGMT_IP --dport $API_PORT -j ACCEPT fi fi -- cgit From bc88c73a4e986289be7835b95ec97ffb7a50f7d7 Mon Sep 17 00:00:00 2001 From: Vishvananda Ishaya Date: Tue, 28 Sep 2010 17:53:27 -0700 Subject: missed a comma --- nova/flags.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/flags.py b/nova/flags.py index 92f6766cf..c32cdd7a4 100644 --- a/nova/flags.py +++ b/nova/flags.py @@ -190,7 +190,7 @@ DEFINE_string('rabbit_virtual_host', '/', 'rabbit virtual host') DEFINE_string('control_exchange', 'nova', 'the main exchange to connect to') DEFINE_string('cc_host', '127.0.0.1', 'ip of api server') DEFINE_integer('cc_port', 8773, 'cloud controller port') -DEFINE_string('ec2_url', 'http://127.0.0.1:8773/services/Cloud' +DEFINE_string('ec2_url', 'http://127.0.0.1:8773/services/Cloud', 'Url to ec2 api server') DEFINE_string('default_image', 'ami-11111', -- cgit From 4c1aa3d96f0c44d3e01864ca3128e9b052d1d7fd Mon Sep 17 00:00:00 2001 From: Michael Gundlach Date: Wed, 29 Sep 2010 10:17:10 -0400 Subject: After update from trunk, a few more exceptions that need to be converted to Faults --- nova/api/rackspace/backup_schedules.py | 7 ++++--- nova/api/rackspace/servers.py | 17 +++++++++-------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/nova/api/rackspace/backup_schedules.py b/nova/api/rackspace/backup_schedules.py index 46da778ee..cb83023bc 100644 --- a/nova/api/rackspace/backup_schedules.py +++ b/nova/api/rackspace/backup_schedules.py @@ -20,6 +20,7 @@ from webob import exc from nova import wsgi from nova.api.rackspace import _id_translator +from nova.api.rackspace import faults import nova.image.service class Controller(wsgi.Controller): @@ -27,12 +28,12 @@ class Controller(wsgi.Controller): pass def index(self, req, server_id): - return exc.HTTPNotFound() + return faults.Fault(exc.HTTPNotFound()) def create(self, req, server_id): """ No actual update method required, since the existing API allows both create and update through a POST """ - return exc.HTTPNotFound() + return faults.Fault(exc.HTTPNotFound()) def delete(self, req, server_id): - return exc.HTTPNotFound() + return faults.Fault(exc.HTTPNotFound()) diff --git a/nova/api/rackspace/servers.py b/nova/api/rackspace/servers.py index 4ab04bde7..888d67542 100644 --- a/nova/api/rackspace/servers.py +++ b/nova/api/rackspace/servers.py @@ -24,6 +24,7 @@ from nova import rpc from nova import utils from nova import wsgi from nova.api.rackspace import _id_translator +from nova.api.rackspace import faults from nova.compute import power_state import nova.image.service @@ -120,7 +121,7 @@ class Controller(wsgi.Controller): if inst: if inst.user_id == user_id: return _entity_detail(inst) - raise exc.HTTPNotFound() + raise faults.Fault(exc.HTTPNotFound()) def delete(self, req, id): """ Destroys a server """ @@ -128,13 +129,13 @@ class Controller(wsgi.Controller): instance = self.db_driver.instance_get(None, id) if instance and instance['user_id'] == user_id: self.db_driver.instance_destroy(None, id) - return exc.HTTPAccepted() - return exc.HTTPNotFound() + return faults.Fault(exc.HTTPAccepted()) + return faults.Fault(exc.HTTPNotFound()) def create(self, req): """ Creates a new server for a given user """ if not req.environ.has_key('inst_dict'): - return exc.HTTPUnprocessableEntity() + return faults.Fault(exc.HTTPUnprocessableEntity()) inst = self._build_server_instance(req) @@ -147,22 +148,22 @@ class Controller(wsgi.Controller): def update(self, req, id): """ Updates the server name or password """ if not req.environ.has_key('inst_dict'): - return exc.HTTPUnprocessableEntity() + return faults.Fault(exc.HTTPUnprocessableEntity()) instance = self.db_driver.instance_get(None, id) if not instance: - return exc.HTTPNotFound() + return faults.Fault(exc.HTTPNotFound()) attrs = req.environ['nova.context'].get('model_attributes', None) if attrs: self.db_driver.instance_update(None, id, _filter_params(attrs)) - return exc.HTTPNoContent() + return faults.Fault(exc.HTTPNoContent()) def action(self, req, id): """ multi-purpose method used to reboot, rebuild, and resize a server """ if not req.environ.has_key('inst_dict'): - return exc.HTTPUnprocessableEntity() + return faults.Fault(exc.HTTPUnprocessableEntity()) def _build_server_instance(self, req): """Build instance data structure and save it to the data store.""" -- cgit From 29eca7e7992fc5c073d70f7c8ca5e5bc03f62af7 Mon Sep 17 00:00:00 2001 From: Michael Gundlach Date: Wed, 29 Sep 2010 11:37:26 -0400 Subject: Limit entity lists by &offset and &limit --- nova/api/rackspace/__init__.py | 20 ++++++++++++++++++++ nova/api/rackspace/flavors.py | 5 ++++- nova/api/rackspace/images.py | 2 ++ nova/api/rackspace/servers.py | 18 ++++++++++++------ nova/tests/api/rackspace/__init__.py | 29 +++++++++++++++++++++++++++++ 5 files changed, 67 insertions(+), 7 deletions(-) diff --git a/nova/api/rackspace/__init__.py b/nova/api/rackspace/__init__.py index 98802663f..48104f6df 100644 --- a/nova/api/rackspace/__init__.py +++ b/nova/api/rackspace/__init__.py @@ -165,3 +165,23 @@ class APIRouter(wsgi.Router): controller=sharedipgroups.Controller()) super(APIRouter, self).__init__(mapper) + + +def limited(items, req): + """Return a slice of items according to requested offset and limit. + + items - a sliceable + req - wobob.Request possibly containing offset and limit GET variables. + offset is where to start in the list, and limit is the maximum number + of items to return. + + If limit is not specified, 0, or > 1000, defaults to 1000. + """ + offset = int(req.GET.get('offset', 0)) + limit = int(req.GET.get('limit', 0)) + if not limit: + limit = 1000 + limit = min(1000, limit) + range_end = offset + limit + return items[offset:range_end] + diff --git a/nova/api/rackspace/flavors.py b/nova/api/rackspace/flavors.py index 3bcf170e5..ba7aa937c 100644 --- a/nova/api/rackspace/flavors.py +++ b/nova/api/rackspace/flavors.py @@ -15,9 +15,11 @@ # License for the specific language governing permissions and limitations # under the License. +from webob import exc + from nova.compute import instance_types from nova import wsgi -from webob import exc +import nova.api.rackspace class Controller(wsgi.Controller): """Flavor controller for the Rackspace API.""" @@ -38,6 +40,7 @@ class Controller(wsgi.Controller): def detail(self, req): """Return all flavors in detail.""" items = [self.show(req, id)['flavor'] for id in self._all_ids()] + items = nova.api.rackspace.limited(items, req) return dict(flavors=items) def show(self, req, id): diff --git a/nova/api/rackspace/images.py b/nova/api/rackspace/images.py index 11b058dec..7da17e6a7 100644 --- a/nova/api/rackspace/images.py +++ b/nova/api/rackspace/images.py @@ -19,6 +19,7 @@ from webob import exc from nova import wsgi from nova.api.rackspace import _id_translator +import nova.api.rackspace import nova.image.service class Controller(wsgi.Controller): @@ -45,6 +46,7 @@ class Controller(wsgi.Controller): def detail(self, req): """Return all public images in detail.""" data = self._service.index() + data = nova.api.rackspace.limited(data, req) for img in data: img['id'] = self._id_translator.to_rs_id(img['id']) return dict(images=data) diff --git a/nova/api/rackspace/servers.py b/nova/api/rackspace/servers.py index 4ab04bde7..958fc86a3 100644 --- a/nova/api/rackspace/servers.py +++ b/nova/api/rackspace/servers.py @@ -25,6 +25,7 @@ from nova import utils from nova import wsgi from nova.api.rackspace import _id_translator from nova.compute import power_state +import nova.api.rackspace import nova.image.service FLAGS = flags.FLAGS @@ -101,16 +102,21 @@ class Controller(wsgi.Controller): def index(self, req): """ Returns a list of server names and ids for a given user """ - user_id = req.environ['nova.context']['user']['id'] - instance_list = self.db_driver.instance_get_all_by_user(None, user_id) - res = [_entity_inst(inst)['server'] for inst in instance_list] - return _entity_list(res) + return self._items(req, entity_maker=_entity_inst) def detail(self, req): """ Returns a list of server details for a given user """ + return self._items(req, entity_maker=_entity_detail) + + def _items(self, req, entity_maker): + """Returns a list of servers for a given user. + + entity_maker - either _entity_detail or _entity_inst + """ user_id = req.environ['nova.context']['user']['id'] - res = [_entity_detail(inst)['server'] for inst in - self.db_driver.instance_get_all_by_user(None, user_id)] + instance_list = self.db_driver.instance_get_all_by_user(None, user_id) + limited_list = nova.api.rackspace.limited(instance_list, req) + res = [entity_maker(inst)['server'] for inst in limited_list] return _entity_list(res) def show(self, req, id): diff --git a/nova/tests/api/rackspace/__init__.py b/nova/tests/api/rackspace/__init__.py index 622cb4335..bfd0f87a7 100644 --- a/nova/tests/api/rackspace/__init__.py +++ b/nova/tests/api/rackspace/__init__.py @@ -17,6 +17,7 @@ import unittest +from nova.api.rackspace import limited from nova.api.rackspace import RateLimitingMiddleware from nova.tests.api.test_helper import * from webob import Request @@ -77,3 +78,31 @@ class RateLimitingMiddlewareTest(unittest.TestCase): self.assertEqual(middleware.limiter.__class__.__name__, "Limiter") middleware = RateLimitingMiddleware(APIStub(), service_host='foobar') self.assertEqual(middleware.limiter.__class__.__name__, "WSGIAppProxy") + + +class LimiterTest(unittest.TestCase): + + def testLimiter(self): + items = range(2000) + req = Request.blank('/') + self.assertEqual(limited(items, req), items[ :1000]) + req = Request.blank('/?offset=0') + self.assertEqual(limited(items, req), items[ :1000]) + req = Request.blank('/?offset=3') + self.assertEqual(limited(items, req), items[3:1003]) + req = Request.blank('/?offset=2005') + self.assertEqual(limited(items, req), []) + req = Request.blank('/?limit=10') + self.assertEqual(limited(items, req), items[ :10]) + req = Request.blank('/?limit=0') + self.assertEqual(limited(items, req), items[ :1000]) + req = Request.blank('/?limit=3000') + self.assertEqual(limited(items, req), items[ :1000]) + req = Request.blank('/?offset=1&limit=3') + self.assertEqual(limited(items, req), items[1:4]) + req = Request.blank('/?offset=3&limit=0') + self.assertEqual(limited(items, req), items[3:1003]) + req = Request.blank('/?offset=3&limit=1500') + self.assertEqual(limited(items, req), items[3:1003]) + req = Request.blank('/?offset=3000&limit=10') + self.assertEqual(limited(items, req), []) -- cgit From 0868bcee453665b1ce24d43a90b3addfaab8c49d Mon Sep 17 00:00:00 2001 From: Cerberus Date: Wed, 29 Sep 2010 12:16:53 -0500 Subject: Server update name and password --- nova/api/rackspace/servers.py | 61 +++++++++++++++++++++++-------------- nova/db/sqlalchemy/models.py | 3 ++ nova/tests/api/rackspace/servers.py | 36 +++++++++++++++++++--- nova/wsgi.py | 9 ++++++ 4 files changed, 82 insertions(+), 27 deletions(-) diff --git a/nova/api/rackspace/servers.py b/nova/api/rackspace/servers.py index 4ab04bde7..c156309bd 100644 --- a/nova/api/rackspace/servers.py +++ b/nova/api/rackspace/servers.py @@ -15,6 +15,7 @@ # License for the specific language governing permissions and limitations # under the License. +import datetime import time from webob import exc @@ -25,11 +26,13 @@ from nova import utils from nova import wsgi from nova.api.rackspace import _id_translator from nova.compute import power_state +from nova.wsgi import Serializer import nova.image.service FLAGS = flags.FLAGS - +flags.DEFINE_string('rs_network_manager', 'nova.network.manager.FlatManager', + 'Networking for rackspace') def translator_instance(): """ Helper method for initializing the image id translator """ @@ -82,7 +85,6 @@ def _entity_inst(inst): class Controller(wsgi.Controller): """ The Server API controller for the Openstack API """ - _serialization_metadata = { 'application/xml': { @@ -146,16 +148,19 @@ class Controller(wsgi.Controller): def update(self, req, id): """ Updates the server name or password """ - if not req.environ.has_key('inst_dict'): + user_id = req.environ['nova.context']['user']['id'] + + inst_dict = self._deserialize(req.body, req) + + if not inst_dict: return exc.HTTPUnprocessableEntity() instance = self.db_driver.instance_get(None, id) - if not instance: + if not instance or instance.user_id != user_id: return exc.HTTPNotFound() - attrs = req.environ['nova.context'].get('model_attributes', None) - if attrs: - self.db_driver.instance_update(None, id, _filter_params(attrs)) + self.db_driver.instance_update(None, id, + _filter_params(inst_dict['server'])) return exc.HTTPNoContent() def action(self, req, id): @@ -170,34 +175,44 @@ class Controller(wsgi.Controller): inst = {} env = req.environ['inst_dict'] + user_id = req.environ['nova.context']['user']['id'] + inst['rs_id'] = _new_rs_id(user_id) image_id = env['server']['imageId'] + opaque_id = translator_instance().from_rs_id(image_id) - inst['name'] = env['server']['server_name'] + inst['name'] = env['server']['name'] inst['image_id'] = opaque_id inst['instance_type'] = env['server']['flavorId'] - - user_id = req.environ['nova.context']['user']['id'] inst['user_id'] = user_id - inst['launch_time'] = ltime inst['mac_address'] = utils.generate_mac() - inst['project_id'] = env['project']['id'] - inst['reservation_id'] = reservation - reservation = utils.generate_uid('r') + #TODO(dietz) These are the attributes I'm unsure of + inst['state_description'] = 'scheduling' + inst['kernel_id'] = '' + inst['ramdisk_id'] = '' + inst['reservation_id'] = utils.generate_uid('r') + inst['key_data'] = '' + inst['key_name'] = '' + inst['security_group'] = '' + + # Flavor related attributes + inst['instance_type'] = '' + inst['memory_mb'] = '' + inst['vcpus'] = '' + inst['local_gb'] = '' - address = self.network.allocate_ip( - inst['user_id'], - inst['project_id'], - mac=inst['mac_address']) + + + #TODO(dietz): This seems necessary. How do these apply across + #the Rackspace implementation? + inst['project_id'] = '' - inst['private_dns_name'] = str(address) - inst['bridge_name'] = network.BridgedNetwork.get_network_for_project( - inst['user_id'], - inst['project_id'], - 'default')['bridge_name'] + self.network_manager = utils.import_object(FLAGS.rs_network_manager) + + address = self.network_manager.allocate_fixed_ip( None, inst['id']) ref = self.db_driver.instance_create(None, inst) inst['id'] = ref.id diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index 3d2460c39..83fc7c852 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -199,6 +199,9 @@ class Instance(BASE, NovaBase): id = Column(Integer, primary_key=True) ec2_id = Column(String(10), unique=True) + rs_id = Column(String(255)) + rs_root_password = Column(String(255)) + user_id = Column(String(255)) project_id = Column(String(255)) diff --git a/nova/tests/api/rackspace/servers.py b/nova/tests/api/rackspace/servers.py index 9fd8e5e88..81682288c 100644 --- a/nova/tests/api/rackspace/servers.py +++ b/nova/tests/api/rackspace/servers.py @@ -95,11 +95,39 @@ class ServersTest(unittest.TestCase): # res = req.get_response(nova.api.API()) # print res - def test_update_server_password(self): - pass - def test_update_server_name(self): - pass + def test_update_bad_params(self): + """ Confirm that update is filtering params """ + inst_dict = dict(cat='leopard', name='server_test', adminPass='bacon') + self.body = json.dumps(dict(server=inst_dict)) + + def server_update(context, id, params): + self.update_called = True + filtered_dict = dict(name='server_test', adminPass='bacon') + self.assertEqual(params, filtered_dict) + + self.stubs.Set(nova.db.api, 'instance_update', + server_update) + + req = webob.Request.blank('/v1.0/servers/1') + req.method = 'PUT' + req.body = self.body + req.get_response(nova.api.API()) + + def test_update_server(self): + inst_dict = dict(name='server_test', adminPass='bacon') + self.body = json.dumps(dict(server=inst_dict)) + + def server_update(context, id, params): + self.assertEqual(params, json.loads(self.body)['server']) + + self.stubs.Set(nova.db.api, 'instance_update', + server_update) + + req = webob.Request.blank('/v1.0/servers/1') + req.method = 'PUT' + req.body = self.body + req.get_response(nova.api.API()) def test_create_backup_schedules(self): req = webob.Request.blank('/v1.0/servers/1/backup_schedules') diff --git a/nova/wsgi.py b/nova/wsgi.py index da9374542..48c4dabc2 100644 --- a/nova/wsgi.py +++ b/nova/wsgi.py @@ -230,6 +230,15 @@ class Controller(object): serializer = Serializer(request.environ, _metadata) return serializer.to_content_type(data) + def _deserialize(self, data, request): + """ + Deserialize the request body to the response type requested in request. + Uses self._serialization_metadata if it exists, which is a dict mapping + MIME types to information needed to serialize to that type. + """ + _metadata = getattr(type(self), "_serialization_metadata", {}) + serializer = Serializer(request.environ, _metadata) + return serializer.deserialize(data) class Serializer(object): """ -- cgit From 9c4319a83a6e7d61ffa6b78e9f17ea35821c5526 Mon Sep 17 00:00:00 2001 From: Cerberus Date: Wed, 29 Sep 2010 12:58:40 -0500 Subject: Make update work correctly --- nova/api/rackspace/servers.py | 20 ++++++++++++++------ nova/db/sqlalchemy/models.py | 2 +- nova/tests/api/rackspace/servers.py | 5 +++-- 3 files changed, 18 insertions(+), 9 deletions(-) diff --git a/nova/api/rackspace/servers.py b/nova/api/rackspace/servers.py index c156309bd..aa955c222 100644 --- a/nova/api/rackspace/servers.py +++ b/nova/api/rackspace/servers.py @@ -34,7 +34,12 @@ FLAGS = flags.FLAGS flags.DEFINE_string('rs_network_manager', 'nova.network.manager.FlatManager', 'Networking for rackspace') -def translator_instance(): +def _instance_id_translator(): + """ Helper method for initializing an id translator for Rackspace instance + ids """ + return _id_translator.RackspaceAPIIdTranslator( "instance", 'nova') + +def _image_id_translator(): """ Helper method for initializing the image id translator """ service = nova.image.service.ImageService.load() return _id_translator.RackspaceAPIIdTranslator( @@ -42,11 +47,11 @@ def translator_instance(): def _filter_params(inst_dict): """ Extracts all updatable parameters for a server update request """ - keys = ['name', 'adminPass'] + keys = dict(name='name', admin_pass='adminPass') new_attrs = {} - for k in keys: - if inst_dict.has_key(k): - new_attrs[k] = inst_dict[k] + for k, v in keys.items(): + if inst_dict.has_key(v): + new_attrs[k] = inst_dict[v] return new_attrs def _entity_list(entities): @@ -174,10 +179,13 @@ class Controller(wsgi.Controller): ltime = time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime()) inst = {} + inst_id_trans = _instance_id_translator() + image_id_trans = _image_id_translator() + env = req.environ['inst_dict'] user_id = req.environ['nova.context']['user']['id'] - inst['rs_id'] = _new_rs_id(user_id) + inst['rs_id'] = inst_id_trans.to_rs_id() image_id = env['server']['imageId'] opaque_id = translator_instance().from_rs_id(image_id) diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index 83fc7c852..2a314b037 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -200,7 +200,7 @@ class Instance(BASE, NovaBase): ec2_id = Column(String(10), unique=True) rs_id = Column(String(255)) - rs_root_password = Column(String(255)) + admin_pass = Column(String(255)) user_id = Column(String(255)) project_id = Column(String(255)) diff --git a/nova/tests/api/rackspace/servers.py b/nova/tests/api/rackspace/servers.py index 81682288c..e08c9c6fd 100644 --- a/nova/tests/api/rackspace/servers.py +++ b/nova/tests/api/rackspace/servers.py @@ -103,7 +103,7 @@ class ServersTest(unittest.TestCase): def server_update(context, id, params): self.update_called = True - filtered_dict = dict(name='server_test', adminPass='bacon') + filtered_dict = dict(name='server_test', admin_pass='bacon') self.assertEqual(params, filtered_dict) self.stubs.Set(nova.db.api, 'instance_update', @@ -119,7 +119,8 @@ class ServersTest(unittest.TestCase): self.body = json.dumps(dict(server=inst_dict)) def server_update(context, id, params): - self.assertEqual(params, json.loads(self.body)['server']) + filtered_dict = dict(name='server_test', admin_pass='bacon') + self.assertEqual(params, filtered_dict) self.stubs.Set(nova.db.api, 'instance_update', server_update) -- cgit From 724c15b6583b47baabd2f01090580cb248ad1244 Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Wed, 29 Sep 2010 14:12:02 -0400 Subject: Remove debuggish print statement. --- bin/nova-manage | 1 - 1 file changed, 1 deletion(-) diff --git a/bin/nova-manage b/bin/nova-manage index 7a9a4c3d1..bf3c67612 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -277,7 +277,6 @@ class UserCommands(object): is_admin = True else: is_admin = False - print "is_admin: %r" % is_admin self.manager.modify_user(name, access_key, secret_key, is_admin) class ProjectCommands(object): -- cgit From 2136f12d29cef9acc7dc6ee0a5901fa3878160f8 Mon Sep 17 00:00:00 2001 From: Michael Gundlach Date: Wed, 29 Sep 2010 15:09:39 -0400 Subject: Make Fault raiseable by inheriting from webob.exc.HTTPException. Change from using self.exception which is reserved by HTTPException to self.wrapped_exc. --- nova/api/rackspace/faults.py | 17 +++++++++-------- nova/tests/api/rackspace/testfaults.py | 10 ++++++++++ 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/nova/api/rackspace/faults.py b/nova/api/rackspace/faults.py index fd6bc3623..32e5c866f 100644 --- a/nova/api/rackspace/faults.py +++ b/nova/api/rackspace/faults.py @@ -17,11 +17,12 @@ import webob.dec +import webob.exc from nova import wsgi -class Fault(wsgi.Application): +class Fault(webob.exc.HTTPException): """An RS API fault response.""" @@ -39,23 +40,23 @@ class Fault(wsgi.Application): def __init__(self, exception): """Create a Fault for the given webob.exc.exception.""" - self.exception = exception + self.wrapped_exc = exception @webob.dec.wsgify def __call__(self, req): - """Generate a WSGI response based on self.exception.""" + """Generate a WSGI response based on the exception passed to ctor.""" # Replace the body with fault details. - code = self.exception.status_int + code = self.wrapped_exc.status_int fault_name = self._fault_names.get(code, "cloudServersFault") fault_data = { fault_name: { 'code': code, - 'message': self.exception.explanation}} + 'message': self.wrapped_exc.explanation}} if code == 413: - retry = self.exception.headers['Retry-After'] + retry = self.wrapped_exc.headers['Retry-After'] fault_data[fault_name]['retryAfter'] = retry # 'code' is an attribute on the fault tag itself metadata = {'application/xml': {'attributes': {fault_name: 'code'}}} serializer = wsgi.Serializer(req.environ, metadata) - self.exception.body = serializer.to_content_type(fault_data) - return self.exception + self.wrapped_exc.body = serializer.to_content_type(fault_data) + return self.wrapped_exc diff --git a/nova/tests/api/rackspace/testfaults.py b/nova/tests/api/rackspace/testfaults.py index 74caffd30..b2931bc98 100644 --- a/nova/tests/api/rackspace/testfaults.py +++ b/nova/tests/api/rackspace/testfaults.py @@ -1,5 +1,6 @@ import unittest import webob +import webob.dec import webob.exc from nova.api.rackspace import faults @@ -28,3 +29,12 @@ class TestFaults(unittest.TestCase): self.assertTrue('sorry' in body_sans_spaces) self.assertTrue('4' in body_sans_spaces) self.assertEqual(resp.headers['Retry-After'], 4) + + def test_raise(self): + @webob.dec.wsgify + def raiser(req): + raise faults.Fault(webob.exc.HTTPNotFound(explanation='whut?')) + req = webob.Request.blank('/.xml') + resp = req.get_response(raiser) + self.assertEqual(resp.status_int, 404) + self.assertTrue('whut?' in resp.body) -- cgit From 072661db01ed196eac92ceb1e942429a0e380e4a Mon Sep 17 00:00:00 2001 From: Michael Gundlach Date: Wed, 29 Sep 2010 15:52:02 -0400 Subject: Support reboot in api.rackspace by extracting reboot function from api.ec2 into api.cloud. --- nova/api/cloud.py | 42 ++++++++++++++++++++++++++++++ nova/api/ec2/cloud.py | 8 ++---- nova/api/rackspace/servers.py | 10 +++++-- nova/tests/api/rackspace/images.py | 1 + nova/tests/api/rackspace/sharedipgroups.py | 1 + 5 files changed, 54 insertions(+), 8 deletions(-) create mode 100644 nova/api/cloud.py diff --git a/nova/api/cloud.py b/nova/api/cloud.py new file mode 100644 index 000000000..345677d4f --- /dev/null +++ b/nova/api/cloud.py @@ -0,0 +1,42 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2010 United States Government as represented by the +# Administrator of the National Aeronautics and Space Administration. +# 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. + +""" +Methods for API calls to control instances via AMQP. +""" + + +from nova import db +from nova import flags +from nova import rpc + +FLAGS = flags.FLAGS + + +def reboot(instance_id, context=None): + """Reboot the given instance. + + #TODO(gundlach) not actually sure what context is used for by ec2 here + -- I think we can just remove it and use None all the time. + """ + instance_ref = db.instance_get_by_ec2_id(None, instance_id) + host = instance_ref['host'] + rpc.cast(db.queue_get_for(context, FLAGS.compute_topic, host), + {"method": "reboot_instance", + "args": {"context": None, + "instance_id": instance_ref['id']}}) diff --git a/nova/api/ec2/cloud.py b/nova/api/ec2/cloud.py index 05e8065f3..4d962fcdd 100644 --- a/nova/api/ec2/cloud.py +++ b/nova/api/ec2/cloud.py @@ -36,6 +36,7 @@ from nova import quota from nova import rpc from nova import utils from nova.compute.instance_types import INSTANCE_TYPES +from nova.api import cloud from nova.api.ec2 import images @@ -664,12 +665,7 @@ class CloudController(object): def reboot_instances(self, context, instance_id, **kwargs): """instance_id is a list of instance ids""" for id_str in instance_id: - instance_ref = db.instance_get_by_ec2_id(context, id_str) - host = instance_ref['host'] - rpc.cast(db.queue_get_for(context, FLAGS.compute_topic, host), - {"method": "reboot_instance", - "args": {"context": None, - "instance_id": instance_ref['id']}}) + cloud.reboot(id_str, context=context) return True def delete_volume(self, context, volume_id, **kwargs): diff --git a/nova/api/rackspace/servers.py b/nova/api/rackspace/servers.py index aa955c222..4f81e25f9 100644 --- a/nova/api/rackspace/servers.py +++ b/nova/api/rackspace/servers.py @@ -24,6 +24,7 @@ from nova import flags from nova import rpc from nova import utils from nova import wsgi +from nova.api import cloud from nova.api.rackspace import _id_translator from nova.compute import power_state from nova.wsgi import Serializer @@ -171,8 +172,13 @@ class Controller(wsgi.Controller): def action(self, req, id): """ multi-purpose method used to reboot, rebuild, and resize a server """ - if not req.environ.has_key('inst_dict'): - return exc.HTTPUnprocessableEntity() + input_dict = self._deserialize(req.body, req) + try: + reboot_type = input_dict['reboot']['type'] + except Exception: + raise faults.Fault(webob.exc.HTTPNotImplemented()) + opaque_id = _instance_id_translator().from_rsapi_id(id) + cloud.reboot(opaque_id) def _build_server_instance(self, req): """Build instance data structure and save it to the data store.""" diff --git a/nova/tests/api/rackspace/images.py b/nova/tests/api/rackspace/images.py index 560d8c898..4c9987e8b 100644 --- a/nova/tests/api/rackspace/images.py +++ b/nova/tests/api/rackspace/images.py @@ -15,6 +15,7 @@ # License for the specific language governing permissions and limitations # under the License. +import stubout import unittest from nova.api.rackspace import images diff --git a/nova/tests/api/rackspace/sharedipgroups.py b/nova/tests/api/rackspace/sharedipgroups.py index b4b281db7..1906b54f5 100644 --- a/nova/tests/api/rackspace/sharedipgroups.py +++ b/nova/tests/api/rackspace/sharedipgroups.py @@ -15,6 +15,7 @@ # License for the specific language governing permissions and limitations # under the License. +import stubout import unittest from nova.api.rackspace import sharedipgroups -- cgit From 128ec65cf39e74b53903dd9788a58c8eb513abe8 Mon Sep 17 00:00:00 2001 From: Cerberus Date: Wed, 29 Sep 2010 16:21:28 -0500 Subject: Server creation up to, but not including, network configuration --- nova/api/rackspace/servers.py | 97 +++++++++++++++++++++------------ nova/db/sqlalchemy/models.py | 1 - nova/tests/api/rackspace/auth.py | 8 ++- nova/tests/api/rackspace/servers.py | 48 +++++++++++----- nova/tests/api/rackspace/test_helper.py | 17 +++++- nova/wsgi.py | 11 ++-- 6 files changed, 123 insertions(+), 59 deletions(-) diff --git a/nova/api/rackspace/servers.py b/nova/api/rackspace/servers.py index aa955c222..cc971adc0 100644 --- a/nova/api/rackspace/servers.py +++ b/nova/api/rackspace/servers.py @@ -25,6 +25,7 @@ from nova import rpc from nova import utils from nova import wsgi from nova.api.rackspace import _id_translator +from nova.compute import instance_types from nova.compute import power_state from nova.wsgi import Serializer import nova.image.service @@ -39,11 +40,11 @@ def _instance_id_translator(): ids """ return _id_translator.RackspaceAPIIdTranslator( "instance", 'nova') -def _image_id_translator(): +def _image_service(): """ Helper method for initializing the image id translator """ service = nova.image.service.ImageService.load() - return _id_translator.RackspaceAPIIdTranslator( - "image", service.__class__.__name__) + return (service, _id_translator.RackspaceAPIIdTranslator( + "image", service.__class__.__name__)) def _filter_params(inst_dict): """ Extracts all updatable parameters for a server update request """ @@ -122,8 +123,11 @@ class Controller(wsgi.Controller): def show(self, req, id): """ Returns server details by server id """ + inst_id_trans = _instance_id_translator() + inst_id = inst_id_trans.from_rs_id(id) + user_id = req.environ['nova.context']['user']['id'] - inst = self.db_driver.instance_get(None, id) + inst = self.db_driver.instance_get_by_ec2_id(None, inst_id) if inst: if inst.user_id == user_id: return _entity_detail(inst) @@ -131,8 +135,11 @@ class Controller(wsgi.Controller): def delete(self, req, id): """ Destroys a server """ + inst_id_trans = _instance_id_translator() + inst_id = inst_id_trans.from_rs_id(id) + user_id = req.environ['nova.context']['user']['id'] - instance = self.db_driver.instance_get(None, id) + instance = self.db_driver.instance_get_by_ec2_id(None, inst_id) if instance and instance['user_id'] == user_id: self.db_driver.instance_destroy(None, id) return exc.HTTPAccepted() @@ -140,10 +147,16 @@ class Controller(wsgi.Controller): def create(self, req): """ Creates a new server for a given user """ - if not req.environ.has_key('inst_dict'): + + env = self._deserialize(req.body, req) + if not env: return exc.HTTPUnprocessableEntity() - inst = self._build_server_instance(req) + #try: + inst = self._build_server_instance(req, env) + #except Exception, e: + # print e + # return exc.HTTPUnprocessableEntity() rpc.cast( FLAGS.compute_topic, { @@ -153,6 +166,8 @@ class Controller(wsgi.Controller): def update(self, req, id): """ Updates the server name or password """ + inst_id_trans = _instance_id_translator() + inst_id = inst_id_trans.from_rs_id(id) user_id = req.environ['nova.context']['user']['id'] inst_dict = self._deserialize(req.body, req) @@ -160,7 +175,7 @@ class Controller(wsgi.Controller): if not inst_dict: return exc.HTTPUnprocessableEntity() - instance = self.db_driver.instance_get(None, id) + instance = self.db_driver.instance_get_by_ec2_id(None, inst_id) if not instance or instance.user_id != user_id: return exc.HTTPNotFound() @@ -174,56 +189,68 @@ class Controller(wsgi.Controller): if not req.environ.has_key('inst_dict'): return exc.HTTPUnprocessableEntity() - def _build_server_instance(self, req): + def _build_server_instance(self, req, env): """Build instance data structure and save it to the data store.""" ltime = time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime()) inst = {} inst_id_trans = _instance_id_translator() - image_id_trans = _image_id_translator() - env = req.environ['inst_dict'] user_id = req.environ['nova.context']['user']['id'] - inst['rs_id'] = inst_id_trans.to_rs_id() + instance_type, flavor = None, None + for k, v in instance_types.INSTANCE_TYPES.iteritems(): + if v['flavorid'] == env['server']['flavorId']: + instance_type, flavor = k, v + break + + if not flavor: + raise Exception, "Flavor not found" + image_id = env['server']['imageId'] - opaque_id = translator_instance().from_rs_id(image_id) + img_service, image_id_trans = _image_service() + + opaque_image_id = image_id_trans.to_rs_id(image_id) + image = img_service.show(opaque_image_id) - inst['name'] = env['server']['name'] - inst['image_id'] = opaque_id - inst['instance_type'] = env['server']['flavorId'] + if not image: + raise Exception, "Image not found" + + inst['server_name'] = env['server']['name'] + inst['image_id'] = opaque_image_id inst['user_id'] = user_id inst['launch_time'] = ltime inst['mac_address'] = utils.generate_mac() + inst['project_id'] = user_id - #TODO(dietz) These are the attributes I'm unsure of inst['state_description'] = 'scheduling' - inst['kernel_id'] = '' - inst['ramdisk_id'] = '' + inst['kernel_id'] = image.get('kernelId', FLAGS.default_kernel) + inst['ramdisk_id'] = image.get('ramdiskId', FLAGS.default_ramdisk) inst['reservation_id'] = utils.generate_uid('r') - inst['key_data'] = '' - inst['key_name'] = '' - inst['security_group'] = '' - # Flavor related attributes - inst['instance_type'] = '' - inst['memory_mb'] = '' - inst['vcpus'] = '' - inst['local_gb'] = '' + #TODO(dietz) this may be ill advised + key_pair_ref = self.db_driver.key_pair_get_all_by_user( + None, user_id)[0] - + inst['key_data'] = key_pair_ref['public_key'] + inst['key_name'] = key_pair_ref['name'] - #TODO(dietz): This seems necessary. How do these apply across - #the Rackspace implementation? - inst['project_id'] = '' + #TODO(dietz) stolen from ec2 api, see TODO there + inst['security_group'] = 'default' - self.network_manager = utils.import_object(FLAGS.rs_network_manager) - - address = self.network_manager.allocate_fixed_ip( None, inst['id']) + # Flavor related attributes + inst['instance_type'] = instance_type + inst['memory_mb'] = flavor['memory_mb'] + inst['vcpus'] = flavor['vcpus'] + inst['local_gb'] = flavor['local_gb'] ref = self.db_driver.instance_create(None, inst) - inst['id'] = ref.id + inst['id'] = inst_id_trans.to_rs_id(ref.ec2_id) + + #self.network_manager = utils.import_object(FLAGS.rs_network_manager) + # + #address = self.network_manager.allocate_fixed_ip( None, inst['id']) return inst diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index 2a314b037..22446fc64 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -199,7 +199,6 @@ class Instance(BASE, NovaBase): id = Column(Integer, primary_key=True) ec2_id = Column(String(10), unique=True) - rs_id = Column(String(255)) admin_pass = Column(String(255)) user_id = Column(String(255)) diff --git a/nova/tests/api/rackspace/auth.py b/nova/tests/api/rackspace/auth.py index a6e10970f..56677c2f4 100644 --- a/nova/tests/api/rackspace/auth.py +++ b/nova/tests/api/rackspace/auth.py @@ -1,12 +1,14 @@ -import webob -import webob.dec +import datetime import unittest + import stubout +import webob +import webob.dec + import nova.api import nova.api.rackspace.auth from nova import auth from nova.tests.api.rackspace import test_helper -import datetime class Test(unittest.TestCase): def setUp(self): diff --git a/nova/tests/api/rackspace/servers.py b/nova/tests/api/rackspace/servers.py index e08c9c6fd..d759eba3c 100644 --- a/nova/tests/api/rackspace/servers.py +++ b/nova/tests/api/rackspace/servers.py @@ -26,6 +26,7 @@ import nova.api.rackspace from nova.api.rackspace import servers import nova.db.api from nova.db.sqlalchemy.models import Instance +import nova.rpc from nova.tests.api.test_helper import * from nova.tests.api.rackspace import test_helper @@ -52,8 +53,11 @@ class ServersTest(unittest.TestCase): test_helper.stub_for_testing(self.stubs) test_helper.stub_out_rate_limiting(self.stubs) test_helper.stub_out_auth(self.stubs) + test_helper.stub_out_id_translator(self.stubs) + test_helper.stub_out_key_pair_funcs(self.stubs) + test_helper.stub_out_image_service(self.stubs) self.stubs.Set(nova.db.api, 'instance_get_all', return_servers) - self.stubs.Set(nova.db.api, 'instance_get', return_server) + self.stubs.Set(nova.db.api, 'instance_get_by_ec2_id', return_server) self.stubs.Set(nova.db.api, 'instance_get_all_by_user', return_servers) @@ -67,9 +71,6 @@ class ServersTest(unittest.TestCase): self.assertEqual(res_dict['server']['id'], '1') self.assertEqual(res_dict['server']['name'], 'server1') - def test_get_backup_schedule(self): - pass - def test_get_server_list(self): req = webob.Request.blank('/v1.0/servers') res = req.get_response(nova.api.API()) @@ -82,19 +83,36 @@ class ServersTest(unittest.TestCase): self.assertEqual(s.get('imageId', None), None) i += 1 - #def test_create_instance(self): - # test_helper.stub_out_image_translator(self.stubs) - # body = dict(server=dict( - # name='server_test', imageId=2, flavorId=2, metadata={}, - # personality = {} - # )) - # req = webob.Request.blank('/v1.0/servers') - # req.method = 'POST' - # req.body = json.dumps(body) + def test_create_instance(self): + def instance_create(context, inst): + class Foo(object): + ec2_id = 1 + return Foo() - # res = req.get_response(nova.api.API()) + def fake_cast(*args, **kwargs): + pass - # print res + self.stubs.Set(nova.db.api, 'instance_create', instance_create) + self.stubs.Set(nova.rpc, 'cast', fake_cast) + + test_helper.stub_out_id_translator(self.stubs) + body = dict(server=dict( + name='server_test', imageId=2, flavorId=2, metadata={}, + personality = {} + )) + req = webob.Request.blank('/v1.0/servers') + req.method = 'POST' + req.body = json.dumps(body) + + res = req.get_response(nova.api.API()) + + self.assertEqual(res.status_int, 200) + + def test_update_no_body(self): + req = webob.Request.blank('/v1.0/servers/1') + req.method = 'PUT' + res = req.get_response(nova.api.API()) + self.assertEqual(res.status_int, 422) def test_update_bad_params(self): """ Confirm that update is filtering params """ diff --git a/nova/tests/api/rackspace/test_helper.py b/nova/tests/api/rackspace/test_helper.py index aa7fb382c..962f0ba4c 100644 --- a/nova/tests/api/rackspace/test_helper.py +++ b/nova/tests/api/rackspace/test_helper.py @@ -9,6 +9,7 @@ from nova import utils from nova import flags import nova.api.rackspace.auth import nova.api.rackspace._id_translator +from nova.image import service from nova.wsgi import Router FLAGS = flags.FLAGS @@ -40,7 +41,21 @@ def fake_wsgi(self, req): req.environ['inst_dict'] = json.loads(req.body) return self.application -def stub_out_image_translator(stubs): + + +def stub_out_key_pair_funcs(stubs): + def key_pair(context, user_id): + return [dict(name='key', public_key='public_key')] + stubs.Set(nova.db.api, 'key_pair_get_all_by_user', + key_pair) + +def stub_out_image_service(stubs): + def fake_image_show(meh, id): + return dict(kernelId=1, ramdiskId=1) + + stubs.Set(nova.image.service.LocalImageService, 'show', fake_image_show) + +def stub_out_id_translator(stubs): class FakeTranslator(object): def __init__(self, id_type, service_name): pass diff --git a/nova/wsgi.py b/nova/wsgi.py index 48c4dabc2..b91d91121 100644 --- a/nova/wsgi.py +++ b/nova/wsgi.py @@ -281,10 +281,13 @@ class Serializer(object): The string must be in the format of a supported MIME type. """ datastring = datastring.strip() - is_xml = (datastring[0] == '<') - if not is_xml: - return json.loads(datastring) - return self._from_xml(datastring) + try: + is_xml = (datastring[0] == '<') + if not is_xml: + return json.loads(datastring) + return self._from_xml(datastring) + except: + return None def _from_xml(self, datastring): xmldata = self.metadata.get('application/xml', {}) -- cgit From 35741ff23bec2b4f301b93128fd018e9c8e70945 Mon Sep 17 00:00:00 2001 From: Cerberus Date: Wed, 29 Sep 2010 17:06:35 -0500 Subject: Servers stuff --- nova/api/rackspace/servers.py | 38 ++++++++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/nova/api/rackspace/servers.py b/nova/api/rackspace/servers.py index 357e0895f..40cd4f691 100644 --- a/nova/api/rackspace/servers.py +++ b/nova/api/rackspace/servers.py @@ -26,6 +26,8 @@ from nova import utils from nova import wsgi from nova.api import cloud from nova.api.rackspace import _id_translator +from nova.api.rackspace import context +from nova.api.rackspace import faults from nova.compute import instance_types from nova.compute import power_state from nova.wsgi import Serializer @@ -254,10 +256,38 @@ class Controller(wsgi.Controller): ref = self.db_driver.instance_create(None, inst) inst['id'] = inst_id_trans.to_rs_id(ref.ec2_id) - #self.network_manager = utils.import_object(FLAGS.rs_network_manager) - # - #address = self.network_manager.allocate_fixed_ip( None, inst['id']) + # TODO(dietz): this isn't explicitly necessary, but the networking + # calls depend on an object with a project_id property + context = context.APIRequestContext(user_id) + + inst['mac_address'] = utils.generate_mac() + + #TODO(dietz) is this necessary? + inst['launch_index'] = 0 + + inst['hostname'] = instance_ref['ec2_id'] + self.db_driver.instance_update(None, inst_id, inst) + self.network_manager = utils.import_object(FLAGS.rs_network_manager) + address = self.network_manager.allocate_fixed_ip(context, inst_id) + + # TODO(vish): This probably should be done in the scheduler + # network is setup when host is assigned + network_topic = self._get_network_topic(user_id) + rpc.call(network_topic, + {"method": "setup_fixed_ip", + "args": {"context": None, + "address": address}}) return inst - + def _get_network_topic(self, user_id): + """Retrieves the network host for a project""" + network_ref = self.db_driver.project_get_network(None, + user_id) + host = network_ref['host'] + if not host: + host = rpc.call(FLAGS.network_topic, + {"method": "set_network_host", + "args": {"context": None, + "project_id": user_id}}) + return self.db_driver.queue_get_for(None, FLAGS.network_topic, host) -- cgit From 33f101c309852f358ab30a8af64ef64a848f16ae Mon Sep 17 00:00:00 2001 From: Cerberus Date: Wed, 29 Sep 2010 17:36:26 -0500 Subject: Some minor cleanup --- nova/api/rackspace/servers.py | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/nova/api/rackspace/servers.py b/nova/api/rackspace/servers.py index c3d56debd..0d285999b 100644 --- a/nova/api/rackspace/servers.py +++ b/nova/api/rackspace/servers.py @@ -213,14 +213,11 @@ class Controller(wsgi.Controller): user_id = req.environ['nova.context']['user']['id'] - instance_type, flavor = None, None - for k, v in instance_types.INSTANCE_TYPES.iteritems(): - if v['flavorid'] == env['server']['flavorId']: - instance_type, flavor = k, v - break + flavor_id = env['server']['flavorId'] - if not flavor: - raise Exception, "Flavor not found" + instance_type, flavor = [(k, v) for k, v in + instance_types.INSTANCE_TYPES.iteritems() + if v['flavorid'] == flavor_id][0] image_id = env['server']['imageId'] @@ -262,10 +259,10 @@ class Controller(wsgi.Controller): ref = self.db_driver.instance_create(None, inst) inst['id'] = inst_id_trans.to_rs_id(ref.ec2_id) - # TODO(dietz): this isn't explicitly necessary, but the networking - # calls depend on an object with a project_id property + # calls depend on an object with a project_id property, and therefore + # should be cleaned up later api_context = context.APIRequestContext(user_id) inst['mac_address'] = utils.generate_mac() -- cgit From 41e940a5cfa62d56e7d4f111827217c64f0ec61d Mon Sep 17 00:00:00 2001 From: Cerberus Date: Wed, 29 Sep 2010 17:40:46 -0500 Subject: Forgot the context module --- nova/api/rackspace/context.py | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 nova/api/rackspace/context.py diff --git a/nova/api/rackspace/context.py b/nova/api/rackspace/context.py new file mode 100644 index 000000000..3a7941f0f --- /dev/null +++ b/nova/api/rackspace/context.py @@ -0,0 +1,32 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2010 United States Government as represented by the +# Administrator of the National Aeronautics and Space Administration. +# 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. + +""" +APIRequestContext +""" + +import random + +class APIRequestContext(object): + """ This is an adapter class to get around all of the assumptions made in + the FlatNetworking """ + def __init__(self, user_id): + class Dummy(object): pass + self.user_id = user_id + self.project = Dummy() + self.project.id = self.user_id -- cgit From 6b932780b6bf10b387ad04be6ec88395cae6b564 Mon Sep 17 00:00:00 2001 From: Cerberus Date: Wed, 29 Sep 2010 17:52:15 -0500 Subject: pylint and pep8 cleanup --- nova/api/rackspace/context.py | 8 +++++--- nova/api/rackspace/servers.py | 8 +++----- nova/tests/api/rackspace/flavors.py | 1 - 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/nova/api/rackspace/context.py b/nova/api/rackspace/context.py index 3a7941f0f..924ee151e 100644 --- a/nova/api/rackspace/context.py +++ b/nova/api/rackspace/context.py @@ -22,11 +22,13 @@ APIRequestContext import random +class Project(object): + def __init__(self, user_id): + self.id = user_id + class APIRequestContext(object): """ This is an adapter class to get around all of the assumptions made in the FlatNetworking """ def __init__(self, user_id): - class Dummy(object): pass self.user_id = user_id - self.project = Dummy() - self.project.id = self.user_id + self.project = Project(user_id) diff --git a/nova/api/rackspace/servers.py b/nova/api/rackspace/servers.py index 0d285999b..965deb402 100644 --- a/nova/api/rackspace/servers.py +++ b/nova/api/rackspace/servers.py @@ -15,7 +15,6 @@ # License for the specific language governing permissions and limitations # under the License. -import datetime import time import webob @@ -31,7 +30,6 @@ from nova.api.rackspace import context from nova.api.rackspace import faults from nova.compute import instance_types from nova.compute import power_state -from nova.wsgi import Serializer import nova.api.rackspace import nova.image.service @@ -201,7 +199,7 @@ class Controller(wsgi.Controller): reboot_type = input_dict['reboot']['type'] except Exception: raise faults.Fault(webob.exc.HTTPNotImplemented()) - opaque_id = _instance_id_translator().from_rsapi_id(id) + opaque_id = _instance_id_translator().from_rs_id(id) cloud.reboot(opaque_id) def _build_server_instance(self, req, env): @@ -273,8 +271,8 @@ class Controller(wsgi.Controller): inst['hostname'] = ref.ec2_id self.db_driver.instance_update(None, inst['id'], inst) - self.network_manager = utils.import_object(FLAGS.rs_network_manager) - address = self.network_manager.allocate_fixed_ip(api_context, + network_manager = utils.import_object(FLAGS.rs_network_manager) + address = network_manager.allocate_fixed_ip(api_context, inst['id']) # TODO(vish): This probably should be done in the scheduler diff --git a/nova/tests/api/rackspace/flavors.py b/nova/tests/api/rackspace/flavors.py index 7bd1ea1c4..d25a2e2be 100644 --- a/nova/tests/api/rackspace/flavors.py +++ b/nova/tests/api/rackspace/flavors.py @@ -38,7 +38,6 @@ class FlavorsTest(unittest.TestCase): def test_get_flavor_list(self): req = webob.Request.blank('/v1.0/flavors') res = req.get_response(nova.api.API()) - print res def test_get_flavor_by_id(self): pass -- cgit From 13a73f2606f4b9dee4e51cccbb7e48c8ce322b76 Mon Sep 17 00:00:00 2001 From: Cerberus Date: Wed, 29 Sep 2010 18:00:24 -0500 Subject: Missed a few attributes while mirroring the ec2 instance spin up --- nova/api/rackspace/servers.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/nova/api/rackspace/servers.py b/nova/api/rackspace/servers.py index 965deb402..2395cb358 100644 --- a/nova/api/rackspace/servers.py +++ b/nova/api/rackspace/servers.py @@ -239,6 +239,9 @@ class Controller(wsgi.Controller): inst['ramdisk_id'] = image.get('ramdiskId', FLAGS.default_ramdisk) inst['reservation_id'] = utils.generate_uid('r') + inst['display_name'] = env['server']['name'] + inst['display_description'] = env['server']['name'] + #TODO(dietz) this may be ill advised key_pair_ref = self.db_driver.key_pair_get_all_by_user( None, user_id)[0] -- cgit From b075b504a0a402fc4e8c24379804633139883008 Mon Sep 17 00:00:00 2001 From: Cerberus Date: Wed, 29 Sep 2010 19:28:14 -0500 Subject: Whoops, forgot the exception handling bit --- nova/api/rackspace/servers.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/nova/api/rackspace/servers.py b/nova/api/rackspace/servers.py index 2395cb358..11efd8aef 100644 --- a/nova/api/rackspace/servers.py +++ b/nova/api/rackspace/servers.py @@ -160,11 +160,10 @@ class Controller(wsgi.Controller): if not env: return faults.Fault(exc.HTTPUnprocessableEntity()) - #try: - inst = self._build_server_instance(req, env) - #except Exception, e: - # print e - # return exc.HTTPUnprocessableEntity() + try: + inst = self._build_server_instance(req, env) + except Exception, e: + return faults.Fault(exc.HTTPUnprocessableEntity()) rpc.cast( FLAGS.compute_topic, { -- cgit From 8c21cc52b1ba007fc12964ea5973290a3f660662 Mon Sep 17 00:00:00 2001 From: Ewan Mellor Date: Thu, 30 Sep 2010 10:21:44 +0100 Subject: Bug #651887: xenapi list_instances completely broken Don't just compute the result for list_instances -- return it! --- nova/virt/xenapi.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nova/virt/xenapi.py b/nova/virt/xenapi.py index 1c6de4403..0d06b1fce 100644 --- a/nova/virt/xenapi.py +++ b/nova/virt/xenapi.py @@ -103,8 +103,8 @@ class XenAPIConnection(object): self._conn.login_with_password(user, pw) def list_instances(self): - result = [self._conn.xenapi.VM.get_name_label(vm) \ - for vm in self._conn.xenapi.VM.get_all()] + return [self._conn.xenapi.VM.get_name_label(vm) \ + for vm in self._conn.xenapi.VM.get_all()] @defer.inlineCallbacks def spawn(self, instance): -- cgit From 75c5ba6aae6a57a61771ed78b6797c90f7da6940 Mon Sep 17 00:00:00 2001 From: Cerberus Date: Thu, 30 Sep 2010 09:22:46 -0500 Subject: Grabbed the wrong copyright info --- nova/api/rackspace/context.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/nova/api/rackspace/context.py b/nova/api/rackspace/context.py index 924ee151e..77394615b 100644 --- a/nova/api/rackspace/context.py +++ b/nova/api/rackspace/context.py @@ -1,7 +1,6 @@ # vim: tabstop=4 shiftwidth=4 softtabstop=4 -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. +# Copyright 2010 OpenStack LLC. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may -- cgit From 76d9510ac496051697f7cdf0d693d5146e83283a Mon Sep 17 00:00:00 2001 From: Ewan Mellor Date: Thu, 30 Sep 2010 16:00:53 +0100 Subject: Bug #652103: NameError in exception handler in sqlalchemy API layer Fix reference to NoResultFound. --- nova/db/sqlalchemy/api.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 9c3caf9af..e63d3b18e 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -30,6 +30,7 @@ from nova.db.sqlalchemy.session import get_session from sqlalchemy import or_ from sqlalchemy.exc import IntegrityError from sqlalchemy.orm import joinedload_all +from sqlalchemy.orm.exc import NoResultFound from sqlalchemy.sql import exists, func FLAGS = flags.FLAGS @@ -348,7 +349,7 @@ def fixed_ip_get_by_address(_context, address): ).filter_by(address=address ).filter_by(deleted=False ).one() - except exc.NoResultFound: + except NoResultFound: new_exc = exception.NotFound("No model for address %s" % address) raise new_exc.__class__, new_exc, sys.exc_info()[2] -- cgit From ae55b04a4b25c6756311f001e36b5bbd76675e8c Mon Sep 17 00:00:00 2001 From: Todd Willey Date: Thu, 30 Sep 2010 18:48:45 -0400 Subject: Mongo bad, swift good. --- nova/objectstore/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nova/objectstore/__init__.py b/nova/objectstore/__init__.py index b8890ac03..ecad9be7c 100644 --- a/nova/objectstore/__init__.py +++ b/nova/objectstore/__init__.py @@ -22,7 +22,7 @@ .. automodule:: nova.objectstore :platform: Unix - :synopsis: Currently a trivial file-based system, getting extended w/ mongo. + :synopsis: Currently a trivial file-based system, getting extended w/ swift. .. moduleauthor:: Jesse Andrews .. moduleauthor:: Devin Carlen .. moduleauthor:: Vishvananda Ishaya -- cgit