Commit e2e13bde authored by Christos Stavrakakis's avatar Christos Stavrakakis
Browse files

Merge branch 'feature-neutron' into develop

parents cc31fa13 3bc1719f
......@@ -34,23 +34,33 @@
#from optparse import make_option
from django.db import transaction
from django.core.management.base import BaseCommand, CommandError
from django.core.management.base import CommandError
from snf_django.management.commands import RemoveCommand
from synnefo.management import common
from synnefo.logic import ips
class Command(BaseCommand):
class Command(RemoveCommand):
args = "<Floating-IP ID> [<Floating-IP ID> ...]"
help = "Release a floating IP"
@common.convert_api_faults
@transaction.commit_on_success
def handle(self, *args, **options):
if not args:
raise CommandError("Please provide a floating-ip address")
raise CommandError("Please provide a floating-ip ID")
floating_ip_id = args[0]
force = options['force']
message = "floating IPs" if len(args) > 1 else "floating IP"
self.confirm_deletion(force, message, args)
floating_ip = common.get_floating_ip_by_id(floating_ip_id,
for_update=True)
ips.delete_floating_ip(floating_ip)
self.stdout.write("Deleted floating IP '%s'.\n" % floating_ip_id)
for floating_ip_id in args:
self.stdout.write("\n")
try:
floating_ip = common.get_floating_ip_by_id(floating_ip_id,
for_update=True)
ips.delete_floating_ip(floating_ip)
self.stdout.write("Deleted floating IP '%s'.\n" %
floating_ip_id)
except CommandError as e:
self.stdout.write("Error -- %s\n" % e.message)
......@@ -166,8 +166,8 @@ class Command(BaseCommand):
if flavor is None:
raise CommandError("flavor is required")
if (subnet is None) and (subnet6 is None):
raise CommandError("subnet or subnet6 is required")
if ((subnet is None) and (subnet6 is None)) and dhcp is not False:
raise CommandError("Cannot set DHCP without subnet or subnet6")
if subnet is None and gateway is not None:
raise CommandError("Cannot use gateway without subnet")
......
......@@ -28,25 +28,37 @@
# policies, either expressed or implied, of GRNET S.A.
#
from django.core.management.base import BaseCommand, CommandError
from django.core.management.base import CommandError
from snf_django.management.commands import RemoveCommand
from snf_django.lib.api import faults
from synnefo.logic import networks
from synnefo.management.common import get_network, convert_api_faults
class Command(BaseCommand):
class Command(RemoveCommand):
can_import_settings = True
args = "<Network ID> [<Network ID> ...]"
help = "Remove a network from the Database, and Ganeti"
@convert_api_faults
def handle(self, *args, **options):
if len(args) < 1:
if not args:
raise CommandError("Please provide a network ID")
network = get_network(args[0], for_update=True)
force = options['force']
message = "networks" if len(args) > 1 else "network"
self.confirm_deletion(force, message, args)
self.stdout.write('Removing network: %s\n' % network.backend_id)
for network_id in args:
self.stdout.write("\n")
try:
network = get_network(network_id, for_update=True)
self.stdout.write('Removing network: %s\n' %
network.backend_id)
networks.delete(network)
networks.delete(network)
self.stdout.write("Successfully submitted Ganeti jobs to"
" remove network %s\n" % network.backend_id)
self.stdout.write("Successfully submitted Ganeti jobs to"
" remove network %s\n" % network.backend_id)
except (CommandError, faults.BadRequest) as e:
self.stdout.write("Error -- %s\n" % e.message)
......@@ -29,16 +29,18 @@
#
from optparse import make_option
from django.core.management.base import BaseCommand, CommandError
from django.core.management.base import CommandError
from synnefo.logic import servers
from synnefo.management import common
from snf_django.management.utils import parse_bool
from snf_django.management.commands import RemoveCommand
class Command(BaseCommand):
class Command(RemoveCommand):
can_import_settings = True
args = "<Port ID> [<Port ID> ...]"
help = "Remove a port from the Database and from the VMs attached to"
option_list = BaseCommand.option_list + (
option_list = RemoveCommand.option_list + (
make_option(
"--wait",
dest="wait",
......@@ -50,15 +52,25 @@ class Command(BaseCommand):
@common.convert_api_faults
def handle(self, *args, **options):
if len(args) < 1:
if not args:
raise CommandError("Please provide a port ID")
port = common.get_port(args[0], for_update=True)
force = options['force']
message = "ports" if len(args) > 1 else "port"
self.confirm_deletion(force, message, args)
servers.delete_port(port)
for port_id in args:
self.stdout.write("\n")
try:
port = common.get_port(port_id, for_update=True)
wait = parse_bool(options["wait"])
if port.machine is not None:
common.wait_server_task(port.machine, wait, stdout=self.stdout)
else:
self.stdout.write("Successfully removed port %s\n" % port)
servers.delete_port(port)
wait = parse_bool(options["wait"])
if port.machine is not None:
common.wait_server_task(port.machine, wait,
stdout=self.stdout)
else:
self.stdout.write("Successfully removed port %s\n" % port)
except CommandError as e:
self.stdout.write("Error -- %s\n" % e.message)
......@@ -33,18 +33,20 @@
from optparse import make_option
from django.core.management.base import BaseCommand, CommandError
from django.core.management.base import CommandError
from synnefo.management.common import (get_vm, convert_api_faults,
wait_server_task)
from synnefo.logic import servers
from snf_django.management.commands import RemoveCommand
from snf_django.management.utils import parse_bool
from snf_django.lib.api import faults
class Command(BaseCommand):
args = "<server ID>"
class Command(RemoveCommand):
args = "<Server ID> [<Server ID> ...]"
help = "Remove a server by deleting the instance from the Ganeti backend."
option_list = BaseCommand.option_list + (
option_list = RemoveCommand.option_list + (
make_option(
'--wait',
dest='wait',
......@@ -56,18 +58,29 @@ class Command(BaseCommand):
@convert_api_faults
def handle(self, *args, **options):
if len(args) != 1:
if not args:
raise CommandError("Please provide a server ID")
server = get_vm(args[0])
force = options['force']
message = "servers" if len(args) > 1 else "server"
self.confirm_deletion(force, message, args)
self.stdout.write("Trying to remove server '%s' from backend '%s'\n" %
(server.backend_vm_id, server.backend))
for server_id in args:
self.stdout.write("\n")
try:
server = get_vm(server_id)
server = servers.destroy(server)
jobID = server.task_job_id
self.stdout.write("Trying to remove server '%s' from backend "
"'%s' \n" % (server.backend_vm_id,
server.backend))
self.stdout.write("Issued OP_INSTANCE_REMOVE with id: %s\n" % jobID)
server = servers.destroy(server)
jobID = server.task_job_id
wait = parse_bool(options["wait"])
wait_server_task(server, wait, self.stdout)
self.stdout.write("Issued OP_INSTANCE_REMOVE with id: %s\n" %
jobID)
wait = parse_bool(options["wait"])
wait_server_task(server, wait, self.stdout)
except (CommandError, faults.BadRequest) as e:
self.stdout.write("Error -- %s\n" % e.message)
......@@ -69,6 +69,7 @@ class Command(ListCommand):
object_class = Subnet
astakos_auth_url = ASTAKOS_AUTH_URL
astakos_token = ASTAKOS_TOKEN
deleted_field = "deleted"
FIELDS = {
"id": ("id", "ID of the subnet"),
......
......@@ -148,7 +148,7 @@ def create_subnet(request):
subnet_dict = subnet_to_dict(sub)
data = json.dumps({'subnet': subnet_dict})
return HttpResponse(data, status=200)
return HttpResponse(data, status=201)
@api.api_method(http_method='GET', user_required=True, logger=log)
......
......@@ -90,7 +90,7 @@ class SubnetTest(BaseAPITest):
}
response = self.post(SUBNETS_URL, test_net.userid,
json.dumps(request), "json")
self.assertSuccess(response)
self.assertSuccess201(response)
resp = json.loads(response.content)['subnet']
self.assertEqual("10.0.3.1", resp['gateway_ip'])
self.assertEqual([{"start": "10.0.3.2", "end": "10.0.3.254"}],
......@@ -109,7 +109,7 @@ class SubnetTest(BaseAPITest):
}
response = self.post(SUBNETS_URL, test_net.userid,
json.dumps(request), "json")
self.assertSuccess(response)
self.assertSuccess201(response)
def test_create_subnet_success_ipv6_with_slaac(self):
"""Test create a subnet with ipv6 and slaac"""
......@@ -123,7 +123,7 @@ class SubnetTest(BaseAPITest):
}
response = self.post(SUBNETS_URL, test_net.userid,
json.dumps(request), "json")
self.assertSuccess(response)
self.assertSuccess201(response)
resp = json.loads(response.content)['subnet']
self.assertEqual("fdc1:4992:1130:fc0b::1", resp['gateway_ip'])
self.assertEqual([], resp['allocation_pools'])
......@@ -154,7 +154,7 @@ class SubnetTest(BaseAPITest):
}
response = self.post(SUBNETS_URL, test_net.userid,
json.dumps(request), "json")
self.assertSuccess(response)
self.assertSuccess201(response)
def test_create_subnet_with_ip_pool_allocation(self):
"""Test create a subnet with an IP pool"""
......@@ -171,7 +171,7 @@ class SubnetTest(BaseAPITest):
}
response = self.post(SUBNETS_URL, test_net.userid,
json.dumps(request), "json")
self.assertSuccess(response)
self.assertSuccess201(response)
def test_create_subnet_with_multiple_ip_pools(self):
"""Test create a subnet with multiple IP pools"""
......@@ -190,7 +190,7 @@ class SubnetTest(BaseAPITest):
}
response = self.post(SUBNETS_URL, test_net.userid,
json.dumps(request), "json")
self.assertSuccess(response)
self.assertSuccess201(response)
resp = json.loads(response.content)['subnet']
self.assertEqual([{"start": "10.0.3.2", "end": "10.0.3.100"},
{"start": "10.0.3.200", "end": "10.0.3.220"}],
......@@ -208,7 +208,7 @@ class SubnetTest(BaseAPITest):
}
response = self.post(SUBNETS_URL, test_net.userid,
json.dumps(request), "json")
self.assertSuccess(response)
self.assertSuccess201(response)
resp = json.loads(response.content)['subnet']
self.assertEqual("10.0.3.150", resp['gateway_ip'])
self.assertEqual([{"start": "10.0.3.1", "end": "10.0.3.149"},
......@@ -250,6 +250,24 @@ class SubnetTest(BaseAPITest):
json.dumps(request), "json")
self.assertConflict(response)
def test_create_subnet_with_gateway_as_the_last_ip_of_subnet(self):
"""Test create a subnet with a gateway, as the last IP of the subnet"""
test_net = mf.NetworkFactory()
request = {
'subnet': {
'network_id': test_net.id,
'cidr': '10.0.3.0/24',
'ip_version': 4,
'gateway_ip': '10.0.3.254'}
}
response = self.post(SUBNETS_URL, test_net.userid,
json.dumps(request), "json")
self.assertSuccess201(response)
resp = json.loads(response.content)['subnet']
self.assertEqual("10.0.3.254", resp['gateway_ip'])
self.assertEqual([{"start": "10.0.3.1", "end": "10.0.3.253"}],
resp['allocation_pools'])
def test_create_subnet_with_ip_pool_end_lower_than_start(self):
"""Test create a subnet with a pool where end is lower than start"""
test_net = mf.NetworkFactory()
......@@ -372,7 +390,7 @@ class SubnetTest(BaseAPITest):
}
response = self.post(SUBNETS_URL, test_net.userid, json.dumps(request),
"json")
self.assertSuccess(response)
self.assertSuccess201(response)
def test_create_subnet_with_dns_nameservers(self):
"""Create a subnet with dns nameservers"""
......@@ -385,7 +403,7 @@ class SubnetTest(BaseAPITest):
}
response = self.post(SUBNETS_URL, test_net.userid, json.dumps(request),
"json")
self.assertSuccess(response)
self.assertSuccess201(response)
def test_create_subnet_with_host_routes(self):
"""Create a subnet with dns nameservers"""
......@@ -398,7 +416,7 @@ class SubnetTest(BaseAPITest):
}
response = self.post(SUBNETS_URL, test_net.userid, json.dumps(request),
"json")
self.assertSuccess(response)
self.assertSuccess201(response)
resp = json.loads(response.content)['subnet']
self.assertEqual(["8.8.8.8", "1.1.1.1"], resp["host_routes"])
......
......@@ -144,7 +144,7 @@ def get_vm(server_id, user_id, for_update=False, non_deleted=False,
if non_suspended and vm.suspended:
raise faults.Forbidden("Administratively Suspended VM")
return vm
except ValueError:
except (ValueError, TypeError):
raise faults.BadRequest('Invalid server ID.')
except VirtualMachine.DoesNotExist:
raise faults.ItemNotFound('Server not found.')
......
......@@ -31,7 +31,8 @@
# interpreted as representing official policies, either expressed
# or implied, of GRNET S.A.
from django.core.management.base import BaseCommand, CommandError
from django.core.management.base import CommandError
from snf_django.management.commands import RemoveCommand
from optparse import make_option
from synnefo.management.common import pool_table_from_type
......@@ -39,11 +40,11 @@ from synnefo.management.common import pool_table_from_type
POOL_CHOICES = ['bridge', 'mac-prefix']
class Command(BaseCommand):
class Command(RemoveCommand):
help = "Remove a pool."
args = "<pool ID>"
output_transaction = True
option_list = BaseCommand.option_list + (
option_list = RemoveCommand.option_list + (
make_option("--type", dest="type",
choices=POOL_CHOICES,
help="Type of pool"
......@@ -58,6 +59,9 @@ class Command(BaseCommand):
pool_table = pool_table_from_type(type_)
force = options['force']
self.confirm_deletion(force, "pool(s)", args)
try:
pool_id = int(args[0])
pool = pool_table.objects.get(id=pool_id)
......
......@@ -61,9 +61,9 @@ def list_subnets(user_id):
"""List all subnets of a user"""
log.debug('list_subnets %s', user_id)
user_subnets = Subnet.objects.filter((Q(network__userid=user_id) &
Q(network__public=False)) |
Q(network__public=True))
query = (((Q(network__userid=user_id) & Q(network__public=False)) |
Q(network__public=True)) & Q(deleted=False))
user_subnets = Subnet.objects.filter(query)
return user_subnets
......@@ -115,11 +115,13 @@ def _create_subnet(network_id, user_id, cidr, name, ipversion=4, gateway=None,
if ipversion == 6:
raise api.faults.Conflict("Can't allocate an IP Pool in IPv6")
elif ipversion == 4:
# Check if the gateway is the first IP of the subnet, in this case
# create a single ip pool
# Check if the gateway is the first IP of the subnet, or the last. In
# that case create a single ip pool.
if gateway_ip:
if int(gateway_ip) - int(cidr_ip) == 1:
allocation_pools = [(gateway_ip + 1, cidr_ip.broadcast - 1)]
elif int(cidr_ip.broadcast) - int(gateway_ip) == 1:
allocation_pools = [(cidr_ip.network + 1, gateway_ip - 1)]
else:
# If the gateway isn't the first available ip, create two
# different ip pools adjacent to said ip
......@@ -232,7 +234,7 @@ def validate_pools(pool_list, cidr, gateway):
if not (gateway < start or gateway > end):
raise api.faults.Conflict("Gateway cannot be in pool range")
# Check if there is a conflict between the IP Poll ranges
# Check if there is a conflict between the IP Pool ranges
end = cidr.network
for pool in pool_list:
if end >= pool[0]:
......
......@@ -150,6 +150,11 @@ def get_subnet(subnet_id, for_update=True):
def get_port(port_id, for_update=True):
"""Get a port object by its ID."""
try:
port_id = int(port_id)
except (ValueError, TypeError):
raise CommandError("Invalid port ID: %s" % port_id)
try:
ports = NetworkInterface.objects
if for_update:
......@@ -193,13 +198,18 @@ def get_floating_ip_log_by_address(address):
def get_floating_ip_by_id(floating_ip_id, for_update=False):
try:
floating_ip_id = int(floating_ip_id)
except (ValueError, TypeError):
raise CommandError("Invalid floating-ip ID: %s" % floating_ip_id)
try:
objects = IPAddress.objects
if for_update:
objects = objects.select_for_update()
return objects.get(floating_ip=True, id=floating_ip_id, deleted=False)
except IPAddress.DoesNotExist:
raise CommandError("Floating IP does not exist.")
raise CommandError("Floating IP %s does not exist." % floating_ip_id)
def check_backend_credentials(clustername, port, username, password):
......
......@@ -39,6 +39,8 @@ from django.core.exceptions import FieldError
from snf_django.management import utils
from snf_django.lib.astakos import UserCache
import distutils
class SynnefoCommand(BaseCommand):
option_list = BaseCommand.option_list + (
......@@ -328,3 +330,30 @@ class ListCommand(BaseCommand):
for field in self.object_class._meta.fields:
table.append((field.name, field.verbose_name, field.help_text))
utils.pprint_table(self.stdout, table, headers)
class RemoveCommand(BaseCommand):
help = "Generic remove command"
option_list = BaseCommand.option_list + (
make_option(
"-f", "--force",
dest="force",
action="store_true",
default=False,
help="Do not prompt for confirmation"),
)
def confirm_deletion(self, force, resource='', args=''):
if force is True:
return True
ids = ', '.join(args)
self.stdout.write("Are you sure you want to delete %s %s?"
" [Y/N] " % (resource, ids))
try:
answer = distutils.util.strtobool(raw_input())
if answer != 1:
raise CommandError("Aborting deletion")
except ValueError:
raise CommandError("Unaccepted input value. Please choose yes/no"
" (y/n).")
......@@ -247,6 +247,9 @@ class BaseAPITest(TestCase):
def assertSuccess(self, response):
self.assertTrue(response.status_code in [200, 202, 203, 204])
def assertSuccess201(self, response):
self.assertEqual(response.status_code, 201)
def assertFault(self, response, status_code, name):
self.assertEqual(response.status_code, status_code)
fault = json.loads(response.content)
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment