Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (9.7 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 (validate_network_info, get_network,
41
                                       get_backend)
42
from synnefo.webproject.management.utils import parse_bool
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
        if options.get('subnet'):
148
            validate_network_info(options)
149

    
150
        # Validate state
151
        state = options.get('state')
152
        if state:
153
            allowed = [x[0] for x in Network.OPER_STATES]
154
            if state not in allowed:
155
                msg = "Invalid state, must be one of %s" % ', '.join(allowed)
156
                raise CommandError(msg)
157

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

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

    
184
        network.save()
185

    
186
        add_reserved_ips = options.get('add_reserved_ips')
187
        remove_reserved_ips = options.get('remove_reserved_ips')
188
        if add_reserved_ips or remove_reserved_ips:
189
            if add_reserved_ips:
190
                add_reserved_ips = add_reserved_ips.split(",")
191
            if remove_reserved_ips:
192
                remove_reserved_ips = remove_reserved_ips.split(",")
193

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

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

    
211
        add_to_backend = options["add_to_backend"]
212
        if add_to_backend is not None:
213
            backend = get_backend(add_to_backend)
214
            network.create_backend_network(backend=backend)
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
        remove_from_backend = options["remove_from_backend"]
220
        if remove_from_backend is not None:
221
            backend = get_backend(remove_from_backend)
222
            if network.nics.filter(machine__backend=backend,
223
                                   machine__deleted=False).exists():
224
                msg = "Can not remove. There are still connected VMs to this"\
225
                      " network"
226
                raise CommandError(msg)
227
            network.action = "DESTROY"
228
            network.save()
229
            delete_network(network, backend, disconnect=True)
230
            msg = "Sent job to delete network '%s' from backend '%s'\n"
231
            self.stdout.write(msg % (network, backend))
232

    
233

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