Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / api / management / commands / network-modify.py @ a4658bbe

History | View | Annotate | Download (9.9 kB)

1
# Copyright 2012-2013 GRNET S.A. All rights reserved.
2
#
3
# Redistribution and use in source and binary forms, with or
4
# without modification, are permitted provided that the following
5
# conditions are met:
6
#
7
#   1. Redistributions of source code must retain the above
8
#      copyright notice, this list of conditions and the following
9
#      disclaimer.
10
#
11
#   2. Redistributions in binary form must reproduce the above
12
#      copyright notice, this list of conditions and the following
13
#      disclaimer in the documentation and/or other materials
14
#      provided with the distribution.
15
#
16
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
# POSSIBILITY OF SUCH DAMAGE.
28
#
29
# The views and conclusions contained in the software and
30
# documentation are those of the authors and should not be
31
# interpreted as representing official policies, either expressed
32
# or implied, of GRNET S.A.
33

    
34
from optparse import make_option
35

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

    
38
from synnefo.db.models import (Network, Backend, BackendNetwork,
39
                               pooled_rapi_client)
40
from synnefo.management.common import (get_network, get_backend)
41
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
"""
55

    
56

    
57
class Command(BaseCommand):
58
    args = "<network id>"
59
    help = HELP_MSG
60
    output_transaction = True
61

    
62
    option_list = BaseCommand.option_list + (
63
        make_option(
64
            '--name',
65
            dest='name',
66
            metavar='NAME',
67
            help="Set network's name"),
68
        make_option(
69
            '--userid',
70
            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"),
84
        make_option(
85
            '--gateway6',
86
            dest='gateway6',
87
            help="Set network's IPv6 gateway"),
88
        make_option(
89
            '--dhcp',
90
            dest='dhcp',
91
            metavar="True|False",
92
            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"),
103
        make_option(
104
            '--mac-prefix',
105
            dest="mac_prefix",
106
            help="Set the MAC prefix"),
107
        make_option(
108
            '--add-reserved-ips',
109
            dest="add_reserved_ips",
110
            help="Comma seperated list of IPs to externally reserve."),
111
        make_option(
112
            '--remove-reserved-ips',
113
            dest="remove_reserved_ips",
114
            help="Comma seperated list of IPs to externally release."),
115
        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
            "--add-to-backend",
124
            dest="add_to_backend",
125
            help="Create a public network to a Ganeti backend."),
126
        make_option(
127
            "--remove-from-backend",
128
            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."),
138
    )
139

    
140
    def handle(self, *args, **options):
141
        if len(args) != 1:
142
            raise CommandError("Please provide a network ID")
143

    
144
        network = get_network(args[0])
145

    
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)
152

    
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)
160

    
161
        floating_ip_pool = options["floating_ip_pool"]
162
        if floating_ip_pool is not None:
163
            floating_ip_pool = parse_bool(floating_ip_pool)
164
        if floating_ip_pool is False and network.floating_ip_pool is True:
165
            if network.floating_ips.filter(deleted=False).exists():
166
                msg = ("Can not make network a non floating IP pool. There are"
167
                       " still reserved floating IPs.")
168
                raise CommandError(msg)
169
        elif floating_ip_pool is True:
170
            for backend in Backend.objects.filter(offline=False):
171
                check_link_availability(backend, network)
172

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

    
187
        network.save()
188

    
189
        add_reserved_ips = options.get('add_reserved_ips')
190
        remove_reserved_ips = options.get('remove_reserved_ips')
191
        if add_reserved_ips or remove_reserved_ips:
192
            if add_reserved_ips:
193
                add_reserved_ips = add_reserved_ips.split(",")
194
            if remove_reserved_ips:
195
                remove_reserved_ips = remove_reserved_ips.split(",")
196

    
197
            for bnetwork in network.backend_networks.all():
198
                with pooled_rapi_client(bnetwork.backend) as c:
199
                    c.ModifyNetwork(network=network.backend_id,
200
                                    add_reserved_ips=add_reserved_ips,
201
                                    remove_reserved_ips=remove_reserved_ips)
202

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

    
214
        add_to_backend = options["add_to_backend"]
215
        if add_to_backend is not None:
216
            backend = get_backend(add_to_backend)
217
            network.create_backend_network(backend=backend)
218
            create_network(network, backend, connect=True)
219
            msg = "Sent job to create network '%s' in backend '%s'\n"
220
            self.stdout.write(msg % (network, backend))
221

    
222
        remove_from_backend = options["remove_from_backend"]
223
        if remove_from_backend is not None:
224
            backend = get_backend(remove_from_backend)
225
            if network.nics.filter(machine__backend=backend,
226
                                   machine__deleted=False).exists():
227
                msg = "Can not remove. There are still connected VMs to this"\
228
                      " network"
229
                raise CommandError(msg)
230
            network.action = "DESTROY"
231
            network.save()
232
            delete_network(network, backend, disconnect=True)
233
            msg = "Sent job to delete network '%s' from backend '%s'\n"
234
            self.stdout.write(msg % (network, backend))
235

    
236

    
237
def check_link_availability(backend, network):
238
    """Check if network link is available in backend."""
239
    with pooled_rapi_client(backend) as c:
240
        ganeti_networks = c.GetNetworks(bulk=True)
241
    name = network.backend_id
242
    mode = network.mode
243
    link = network.link
244
    for gnet in ganeti_networks:
245
        if (gnet["name"] != name and
246
           (mode, link) in [(m, l) for (_, m, l) in gnet["group_list"]]):
247
            msg = "Can not create network '%s' in backend '%s'. Link '%s'" \
248
                  " is already used by network '%s" % \
249
                  (network, backend, gnet["name"])
250
            raise CommandError(msg)