Revision 2a2f9ffa snf-cyclades-app/synnefo/api/management/commands/network-modify.py

b/snf-cyclades-app/synnefo/api/management/commands/network-modify.py
35 35

  
36 36
from django.core.management.base import BaseCommand, CommandError
37 37

  
38
from synnefo.db.models import (Network, Backend, BackendNetwork,
39
                               pooled_rapi_client)
38
from synnefo.db.models import (Backend, BackendNetwork, pooled_rapi_client)
40 39
from synnefo.management.common import (get_network, get_backend)
41 40
from snf_django.management.utils import parse_bool
42
from synnefo.logic import networks
43
from synnefo.logic.backend import create_network, delete_network
44

  
45
HELP_MSG = """Modify a network.
46

  
47
This management command will only modify the state of the network in Cyclades
48
DB. The state of the network in the Ganeti backends will remain unchanged. You
49
should manually modify the network in all the backends, to synchronize the
50
state of DB and Ganeti.
51

  
52
The only exception is add_reserved_ips and remove_reserved_ips options, which
53
modify the IP pool in the Ganeti backends.
54
"""
41
from synnefo.logic import networks, backend as backend_mod
42
from django.db import transaction
55 43

  
56 44

  
57 45
class Command(BaseCommand):
58 46
    args = "<network id>"
59
    help = HELP_MSG
60
    output_transaction = True
47
    help = "Modify a network."
61 48

  
62 49
    option_list = BaseCommand.option_list + (
63 50
        make_option(
64 51
            '--name',
65 52
            dest='name',
66 53
            metavar='NAME',
67
            help="Set network's name"),
54
            help="Rename a network"),
68 55
        make_option(
69 56
            '--userid',
70 57
            dest='userid',
71
            help="Set the userid of the network owner"),
72
        make_option(
73
            '--subnet',
74
            dest='subnet',
75
            help="Set network's subnet"),
76
        make_option(
77
            '--gateway',
78
            dest='gateway',
79
            help="Set network's gateway"),
80
        make_option(
81
            '--subnet6',
82
            dest='subnet6',
83
            help="Set network's IPv6 subnet"),
58
            help="Change the owner of the network."),
84 59
        make_option(
85
            '--gateway6',
86
            dest='gateway6',
87
            help="Set network's IPv6 gateway"),
88
        make_option(
89
            '--dhcp',
90
            dest='dhcp',
60
            "--drained",
61
            dest="drained",
91 62
            metavar="True|False",
92 63
            choices=["True", "False"],
93
            help="Set if network will use nfdhcp"),
94
        make_option(
95
            '--state',
96
            dest='state',
97
            metavar='STATE',
98
            help="Set network's state"),
99
        make_option(
100
            '--link',
101
            dest='link',
102
            help="Set the connectivity link"),
64
            help="Set as drained to exclude for IP allocation."
65
                 " Only used for public networks."),
103 66
        make_option(
104
            '--mac-prefix',
105
            dest="mac_prefix",
106
            help="Set the MAC prefix"),
67
            "--floating-ip-pool",
68
            dest="floating_ip_pool",
69
            metavar="True|False",
70
            choices=["True", "False"],
71
            help="Convert network to a floating IP pool. During this"
72
                 " conversation the network will be created to all"
73
                 " available Ganeti backends."),
107 74
        make_option(
108 75
            '--add-reserved-ips',
109 76
            dest="add_reserved_ips",
......
113 80
            dest="remove_reserved_ips",
114 81
            help="Comma seperated list of IPs to externally release."),
115 82
        make_option(
116
            "--drained",
117
            dest="drained",
118
            metavar="True|False",
119
            choices=["True", "False"],
120
            help="Set as drained to exclude for IP allocation."
121
                 " Only used for public networks."),
122
        make_option(
123 83
            "--add-to-backend",
124 84
            dest="add_to_backend",
125
            help="Create a public network to a Ganeti backend."),
85
            metavar="BACKEND_ID",
86
            help="Create a network to a Ganeti backend."),
126 87
        make_option(
127 88
            "--remove-from-backend",
128 89
            dest="remove_from_backend",
129
            help="Remove a public network from a Ganeti backend."),
130
        make_option(
131
            "--floating-ip-pool",
132
            dest="floating_ip_pool",
133
            metavar="True|False",
134
            choices=["True", "False"],
135
            help="Convert network to a floating IP pool. During this"
136
                 " conversation the network will be created to all"
137
                 " available Ganeti backends."),
90
            metavar="BACKEND_ID",
91
            help="Remove a network from a Ganeti backend."),
138 92
    )
139 93

  
94
    @transaction.commit_on_success
140 95
    def handle(self, *args, **options):
141 96
        if len(args) != 1:
142 97
            raise CommandError("Please provide a network ID")
143 98

  
144 99
        network = get_network(args[0])
145 100

  
146
        # Validate subnet
147
        subnet = options["subnet"] or network.subnet
148
        gateway = options["gateway"] or network.gateway
149
        subnet6 = options["subnet6"] or network.subnet6
150
        gateway6 = options["gateway6"] or network.gateway6
151
        networks.validate_network_params(subnet, gateway, subnet6, gateway6)
101
        new_name = options.get("name")
102
        if new_name is not None:
103
            old_name = network.name
104
            network = networks.rename(network, new_name)
105
            self.stdout.write("Renamed network '%s' from '%s' to '%s'.\n" %
106
                              (network, old_name, new_name))
152 107

  
153
        # Validate state
154
        state = options.get('state')
155
        if state:
156
            allowed = [x[0] for x in Network.OPER_STATES]
157
            if state not in allowed:
158
                msg = "Invalid state, must be one of %s" % ', '.join(allowed)
159
                raise CommandError(msg)
108
        drained = options.get("drained")
109
        if drained is not None:
110
            drained = parse_bool(drained)
111
            network.drained = drained
112
            network.save()
113
            self.stdout.write("Set network '%s' as drained=%s.\n" %
114
                              (network, drained))
115

  
116
        new_owner = options.get("userid")
117
        if new_owner is not None:
118
            if "@" in new_owner:
119
                raise CommandError("Invalid owner UUID.")
120
            old_owner = network.userid
121
            network.userid = new_owner
122
            network.save()
123
            msg = "Changed the owner of network '%s' from '%s' to '%s'.\n"
124
            self.stdout.write(msg % (network, old_owner, new_owner))
160 125

  
161 126
        floating_ip_pool = options["floating_ip_pool"]
162 127
        if floating_ip_pool is not None:
163 128
            floating_ip_pool = parse_bool(floating_ip_pool)
164
            options["floating_ip_pool"] = floating_ip_pool
165
        if floating_ip_pool is False and network.floating_ip_pool is True:
166
            if network.floating_ips.filter(deleted=False).exists():
167
                msg = ("Can not make network a non floating IP pool. There are"
168
                       " still reserved floating IPs.")
169
                raise CommandError(msg)
170
        elif floating_ip_pool is True:
171
            existing =\
172
                network.backend_networks.filter(operstate="ACTIVE")\
173
                                        .values_list("backend", flat=True)
174
            for backend in Backend.objects.filter(offline=False)\
175
                                          .exclude(id__in=existing):
176
                check_link_availability(backend, network)
177

  
178
        dhcp = options.get("dhcp")
179
        if dhcp:
180
            options["dhcp"] = parse_bool(dhcp)
181
        drained = options.get("drained")
182
        if drained:
183
            options["drained"] = parse_bool(drained)
184
        fields = ('name', 'userid', 'subnet', 'gateway', 'subnet6', 'gateway6',
185
                  'dhcp', 'state', 'link', 'mac_prefix', 'drained',
186
                  'floating_ip_pool')
187
        for field in fields:
188
            value = options.get(field, None)
189
            if value is not None:
190
                network.__setattr__(field, value)
191

  
192
        network.save()
129
            if floating_ip_pool is False and network.floating_ip_pool is True:
130
                if network.floating_ips.filter(deleted=False).exists():
131
                    msg = ("Can not make network a non floating IP pool."
132
                           " There are still reserved floating IPs.")
133
                    raise CommandError(msg)
134
            network.floating_ip_pool = floating_ip_pool
135
            network.save()
136
            self.stdout.write("Set network '%s' as floating-ip-pool=%s.\n" %
137
                              (network, floating_ip_pool))
138
            if floating_ip_pool is True:
139
                for backend in Backend.objects.filter(offline=False):
140
                    try:
141
                        bnet = network.backend_networks.get(backend=backend)
142
                    except BackendNetwork.DoesNotExist:
143
                        bnet = network.create_backend_network(backend=backend)
144
                    if bnet.operstate != "ACTIVE":
145
                        backend_mod.create_network(network, backend,
146
                                                   connect=True)
147
                        msg = ("Sent job to create network '%s' in backend"
148
                               " '%s'\n" % (network, backend))
149
                        self.stdout.write(msg)
193 150

  
194 151
        add_reserved_ips = options.get('add_reserved_ips')
195 152
        remove_reserved_ips = options.get('remove_reserved_ips')
......
199 156
            if remove_reserved_ips:
200 157
                remove_reserved_ips = remove_reserved_ips.split(",")
201 158

  
202
            for bnetwork in network.backend_networks.all():
159
            for bnetwork in network.backend_networks.filter(offline=False):
203 160
                with pooled_rapi_client(bnetwork.backend) as c:
204 161
                    c.ModifyNetwork(network=network.backend_id,
205 162
                                    add_reserved_ips=add_reserved_ips,
206 163
                                    remove_reserved_ips=remove_reserved_ips)
207 164

  
208
        if floating_ip_pool is True:
209
            for backend in Backend.objects.filter(offline=False):
210
                try:
211
                    bnet = network.backend_networks.get(backend=backend)
212
                except BackendNetwork.DoesNotExist:
213
                    bnet = network.create_backend_network(backend=backend)
214
                if bnet.operstate != "ACTIVE":
215
                    create_network(network, backend, connect=True)
216
                    msg = "Sent job to create network '%s' in backend '%s'\n"
217
                    self.stdout.write(msg % (network, backend))
218

  
219 165
        add_to_backend = options["add_to_backend"]
220 166
        if add_to_backend is not None:
221 167
            backend = get_backend(add_to_backend)
222 168
            network.create_backend_network(backend=backend)
223
            create_network(network, backend, connect=True)
169
            backend_mod.create_network(network, backend, connect=True)
224 170
            msg = "Sent job to create network '%s' in backend '%s'\n"
225 171
            self.stdout.write(msg % (network, backend))
226 172

  
......
234 180
                raise CommandError(msg)
235 181
            network.action = "DESTROY"
236 182
            network.save()
237
            delete_network(network, backend, disconnect=True)
183
            backend_mod.delete_network(network, backend, disconnect=True)
238 184
            msg = "Sent job to delete network '%s' from backend '%s'\n"
239 185
            self.stdout.write(msg % (network, backend))
240

  
241

  
242
def check_link_availability(backend, network):
243
    """Check if network link is available in backend."""
244
    with pooled_rapi_client(backend) as c:
245
        ganeti_networks = c.GetNetworks(bulk=True)
246
    name = network.backend_id
247
    mode = network.mode
248
    link = network.link
249
    for gnet in ganeti_networks:
250
        if (gnet["name"] != name and
251
            reduce(lambda x, y: x or y,
252
                   ["(%s, %s)" % (mode, link) in gnet["group_list"]],
253
                   False)):
254
           # Ganeti >= 2.7
255
           #(mode, link) in [(m, l) for (_, m, l) in gnet["group_list"]]):
256
            msg = "Can not create network '%s' in backend '%s'. Link '%s'" \
257
                  " is already used by network '%s" % \
258
                  (network, backend, link, gnet["name"])
259
            raise CommandError(msg)

Also available in: Unified diff