commit 9d1e64800fdf78595d0bb1fc70083ce4a44cefb0
Author: Marios Kogias <marioskogias@grnet.gr>
Date:   Mon Oct 21 14:09:03 2013 +0300

    cyclades: snf-manage floating-ip-release <address>

diff --git a/snf-cyclades-app/synnefo/api/management/commands/floating-ip-release.py b/snf-cyclades-app/synnefo/api/management/commands/floating-ip-release.py
new file mode 100644
index 0000000..2642ea0
--- /dev/null
+++ b/snf-cyclades-app/synnefo/api/management/commands/floating-ip-release.py
@@ -0,0 +1,73 @@
+# Copyright 2012 GRNET S.A. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or
+# without modification, are permitted provided that the following
+# conditions are met:
+#
+#   1. Redistributions of source code must retain the above
+#      copyright notice, this list of conditions and the following
+#      disclaimer.
+#
+#   2. Redistributions in binary form must reproduce the above
+#      copyright notice, this list of conditions and the following
+#      disclaimer in the documentation and/or other materials
+#      provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
+# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+# The views and conclusions contained in the software and
+# documentation are those of the authors and should not be
+# interpreted as representing official policies, either expressed
+# or implied, of GRNET S.A.
+
+from optparse import make_option
+
+from django.db import transaction
+from django.core.management.base import BaseCommand, CommandError
+
+from synnefo.management.common import get_floating_ip_by_address
+from synnefo import quotas
+
+class Command(BaseCommand):
+    can_import_settings = True
+    output_transaction = True
+
+    help = "Release a floating IP"
+
+    @transaction.commit_on_success
+    def handle(self, *args, **options):
+        if not args:
+            raise CommandError("Please provide a floating-ip address")
+
+        address = args[0]
+
+        floating_ip = get_floating_ip_by_address(address, for_update=True)
+        if floating_ip.nic:
+            # This is safe, you also need for_update to attach floating IP to
+            # instance.
+            msg = "Floating IP '%s' is attached to instance." % floating_ip.id
+            raise CommandError(msg)
+
+        # Return the address of the floating IP back to pool
+        floating_ip.release_address()
+        # And mark the floating IP as deleted
+        floating_ip.deleted = True
+        floating_ip.save()
+        # Release quota for floating IP
+        quotas.issue_and_accept_commission(floating_ip, delete=True)
+        transaction.commit()
+        # Delete the floating IP from DB
+        floating_ip.delete()
+
+        self.stdout.write("Deleted floating IP '%s'.\n" % address)
diff --git a/snf-cyclades-app/synnefo/api/util.py b/snf-cyclades-app/synnefo/api/util.py
index 57d8192..6315fc5 100644
--- a/snf-cyclades-app/synnefo/api/util.py
+++ b/snf-cyclades-app/synnefo/api/util.py
@@ -270,8 +270,12 @@ def get_floating_ip_by_id(userid, floating_ip_id, for_update=False):
         objects = IPAddress.objects
         if for_update:
             objects = objects.select_for_update()
-        return objects.get(id=floating_ip_id, floating_ip=True, userid=userid,
-                           deleted=False)
+        if not userid:
+            return objects.get(id=floating_ip_id, floating_ip=True,
+                               deleted=False)
+        else:
+            return objects.get(id=floating_ip_id, floating_ip=True,
+                               userid=userid, deleted=False)
     except IPAddress.DoesNotExist:
         raise faults.ItemNotFound("Floating IP %s does not exist." %
                                   floating_ip_id)
diff --git a/snf-cyclades-app/synnefo/management/common.py b/snf-cyclades-app/synnefo/management/common.py
index 2303a13..a63e59a 100644
--- a/snf-cyclades-app/synnefo/management/common.py
+++ b/snf-cyclades-app/synnefo/management/common.py
@@ -32,7 +32,8 @@
 # or implied, of GRNET S.A.
 
 from django.core.management import CommandError
-from synnefo.db.models import Backend, VirtualMachine, Network, Flavor, Subnet
+from synnefo.db.models import (Backend, VirtualMachine, Network,
+                               Flavor, IPAddress)
 from functools import wraps
 
 from snf_django.lib.api import faults
@@ -149,6 +150,16 @@ def get_flavor(flavor_id):
                            " available flavor IDs." % flavor_id)
 
 
+def get_floating_ip_by_address(address, for_update=False):
+    try:
+        objects = IPAddress.objects
+        if for_update:
+            objects = objects.select_for_update()
+        return objects.get(floating_ip=True, address=address, deleted=False)
+    except IPAddress.DoesNotExist:
+        raise CommandError("Floating IP does not exist.")
+
+
 def check_backend_credentials(clustername, port, username, password):
     try:
         client = GanetiRapiClient(clustername, port, username, password)
