Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (9.6 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
import re
35
from optparse import make_option
36

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

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

    
46
HELP_MSG = """Modify a network.
47

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

53
The only exception is add_reserved_ips and remove_reserved_ips options, which
54
modify the IP pool in the Ganeti backends.
55
"""
56

    
57

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

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

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

    
145
        network = get_network(args[0])
146

    
147
        # Validate subnet
148
        if options.get('subnet'):
149
            validate_network_info(options)
150

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

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

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

    
183
        network.save()
184

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

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

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

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

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

    
232

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