Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / api / management / commands / server-import.py @ c6fe2f41

History | View | Annotate | Download (9.5 kB)

1 b344501e Christos Stavrakakis
# Copyright 2012 GRNET S.A. All rights reserved.
2 b344501e Christos Stavrakakis
#
3 b344501e Christos Stavrakakis
# Redistribution and use in source and binary forms, with or
4 b344501e Christos Stavrakakis
# without modification, are permitted provided that the following
5 b344501e Christos Stavrakakis
# conditions are met:
6 b344501e Christos Stavrakakis
#
7 b344501e Christos Stavrakakis
#   1. Redistributions of source code must retain the above
8 b344501e Christos Stavrakakis
#      copyright notice, this list of conditions and the following
9 b344501e Christos Stavrakakis
#      disclaimer.
10 b344501e Christos Stavrakakis
#
11 b344501e Christos Stavrakakis
#   2. Redistributions in binary form must reproduce the above
12 b344501e Christos Stavrakakis
#      copyright notice, this list of conditions and the following
13 b344501e Christos Stavrakakis
#      disclaimer in the documentation and/or other materials
14 b344501e Christos Stavrakakis
#      provided with the distribution.
15 b344501e Christos Stavrakakis
#
16 b344501e Christos Stavrakakis
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 b344501e Christos Stavrakakis
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 b344501e Christos Stavrakakis
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 b344501e Christos Stavrakakis
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 b344501e Christos Stavrakakis
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 b344501e Christos Stavrakakis
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 b344501e Christos Stavrakakis
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 b344501e Christos Stavrakakis
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 b344501e Christos Stavrakakis
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 b344501e Christos Stavrakakis
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 b344501e Christos Stavrakakis
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 b344501e Christos Stavrakakis
# POSSIBILITY OF SUCH DAMAGE.
28 b344501e Christos Stavrakakis
#
29 b344501e Christos Stavrakakis
# The views and conclusions contained in the software and
30 b344501e Christos Stavrakakis
# documentation are those of the authors and should not be
31 b344501e Christos Stavrakakis
# interpreted as representing official policies, either expressed
32 b344501e Christos Stavrakakis
# or implied, of GRNET S.A.
33 b344501e Christos Stavrakakis
34 b344501e Christos Stavrakakis
from optparse import make_option
35 b344501e Christos Stavrakakis
36 b344501e Christos Stavrakakis
from django.core.management.base import BaseCommand, CommandError
37 b344501e Christos Stavrakakis
from synnefo.management import common
38 b344501e Christos Stavrakakis
39 b344501e Christos Stavrakakis
from synnefo.db.models import VirtualMachine, Network, Flavor
40 b344501e Christos Stavrakakis
from synnefo.logic.utils import id_from_network_name, id_from_instance_name
41 b344501e Christos Stavrakakis
from synnefo.logic.backend import wait_for_job
42 b344501e Christos Stavrakakis
from synnefo.logic.rapi import GanetiApiError
43 b344501e Christos Stavrakakis
from synnefo.api.util import allocate_public_address
44 c34b2908 Christos Stavrakakis
from synnefo import quotas
45 b344501e Christos Stavrakakis
46 b344501e Christos Stavrakakis
import sys
47 b344501e Christos Stavrakakis
48 b344501e Christos Stavrakakis
49 b344501e Christos Stavrakakis
HELP_MSG = """
50 b344501e Christos Stavrakakis

51 b344501e Christos Stavrakakis
Import an existing Ganeti instance into Synnefo, with the attributes specified
52 b344501e Christos Stavrakakis
by the command line options. In order to be imported, the instance will be
53 b344501e Christos Stavrakakis
turned off, renamed and then turned on again.
54 b344501e Christos Stavrakakis

55 b344501e Christos Stavrakakis
Importing an instance will fail, if the instance has NICs that are connected to
56 b344501e Christos Stavrakakis
a network not belonging to Synnefo. You can either manually modify the instance
57 b344501e Christos Stavrakakis
or use --new-nics option, that will remove all old NICs, and create a new one
58 b344501e Christos Stavrakakis
connected to a public network of Synnefo.
59 b344501e Christos Stavrakakis

60 b344501e Christos Stavrakakis
"""
61 b344501e Christos Stavrakakis
62 b344501e Christos Stavrakakis
63 b344501e Christos Stavrakakis
class Command(BaseCommand):
64 b344501e Christos Stavrakakis
    help = "Import an existing Ganeti VM into Synnefo." + HELP_MSG
65 b344501e Christos Stavrakakis
    args = "<ganeti_instance_name>"
66 b344501e Christos Stavrakakis
    output_transaction = True
67 b344501e Christos Stavrakakis
68 b344501e Christos Stavrakakis
    option_list = BaseCommand.option_list + (
69 9621c777 Christos Stavrakakis
        make_option(
70 9621c777 Christos Stavrakakis
            "--backend-id",
71 9621c777 Christos Stavrakakis
            dest="backend_id",
72 9621c777 Christos Stavrakakis
            help="Unique identifier of the Ganeti backend that"
73 9621c777 Christos Stavrakakis
                 " hosts the VM. Use snf-manage backend-list to"
74 9621c777 Christos Stavrakakis
                 " find out available backends."),
75 9621c777 Christos Stavrakakis
        make_option(
76 9621c777 Christos Stavrakakis
            "--user-id",
77 9621c777 Christos Stavrakakis
            dest="user_id",
78 9621c777 Christos Stavrakakis
            help="Unique identifier of the owner of the server"),
79 9621c777 Christos Stavrakakis
        make_option(
80 9621c777 Christos Stavrakakis
            "--image-id",
81 9621c777 Christos Stavrakakis
            dest="image_id",
82 9621c777 Christos Stavrakakis
            default=None,
83 9621c777 Christos Stavrakakis
            help="Unique identifier of the image."
84 9621c777 Christos Stavrakakis
                 " Use snf-manage image-list to find out"
85 9621c777 Christos Stavrakakis
                 " available images."),
86 9621c777 Christos Stavrakakis
        make_option(
87 9621c777 Christos Stavrakakis
            "--flavor-id",
88 9621c777 Christos Stavrakakis
            dest="flavor_id",
89 9621c777 Christos Stavrakakis
            help="Unique identifier of the flavor"
90 9621c777 Christos Stavrakakis
                 " Use snf-manage flavor-list to find out"
91 9621c777 Christos Stavrakakis
                 " available flavors."),
92 9621c777 Christos Stavrakakis
        make_option(
93 9621c777 Christos Stavrakakis
            "--new-nics",
94 9621c777 Christos Stavrakakis
            dest='new_nics',
95 9621c777 Christos Stavrakakis
            default=False,
96 9621c777 Christos Stavrakakis
            action="store_true",
97 9621c777 Christos Stavrakakis
            help="Remove old NICs of instance, and create"
98 9621c777 Christos Stavrakakis
                 " a new NIC connected to a public network of"
99 9621c777 Christos Stavrakakis
                 " Synnefo.")
100 9621c777 Christos Stavrakakis
    )
101 b344501e Christos Stavrakakis
102 b344501e Christos Stavrakakis
    REQUIRED = ("user-id", "backend-id", "image-id", "flavor-id")
103 b344501e Christos Stavrakakis
104 b344501e Christos Stavrakakis
    def handle(self, *args, **options):
105 b344501e Christos Stavrakakis
        if len(args) < 1:
106 b344501e Christos Stavrakakis
            raise CommandError("Please specify a Ganeti instance")
107 b344501e Christos Stavrakakis
108 b344501e Christos Stavrakakis
        instance_name = args[0]
109 b344501e Christos Stavrakakis
110 b344501e Christos Stavrakakis
        try:
111 b344501e Christos Stavrakakis
            id_from_instance_name(instance_name)
112 b344501e Christos Stavrakakis
            raise CommandError("%s is already a synnefo instance")
113 b344501e Christos Stavrakakis
        except:
114 b344501e Christos Stavrakakis
            pass
115 b344501e Christos Stavrakakis
116 b344501e Christos Stavrakakis
        user_id = options['user_id']
117 b344501e Christos Stavrakakis
        backend_id = options['backend_id']
118 b344501e Christos Stavrakakis
        image_id = options['image_id']
119 b344501e Christos Stavrakakis
        flavor_id = options['flavor_id']
120 b344501e Christos Stavrakakis
        new_public_nic = options['new_nics']
121 b344501e Christos Stavrakakis
122 b344501e Christos Stavrakakis
        for field in self.REQUIRED:
123 b344501e Christos Stavrakakis
            if not locals()[field.replace("-", "_")]:
124 b344501e Christos Stavrakakis
                raise CommandError(field + " is mandatory")
125 b344501e Christos Stavrakakis
126 b344501e Christos Stavrakakis
        import_server(instance_name, backend_id, flavor_id, image_id, user_id,
127 b344501e Christos Stavrakakis
                      new_public_nic, self.stdout)
128 b344501e Christos Stavrakakis
129 b344501e Christos Stavrakakis
130 b344501e Christos Stavrakakis
def import_server(instance_name, backend_id, flavor_id, image_id, user_id,
131 b344501e Christos Stavrakakis
                  new_public_nic, stream=sys.stdout):
132 b344501e Christos Stavrakakis
    flavor = common.get_flavor(flavor_id)
133 b344501e Christos Stavrakakis
    backend = common.get_backend(backend_id)
134 b344501e Christos Stavrakakis
135 b344501e Christos Stavrakakis
    backend_client = backend.get_client()
136 b344501e Christos Stavrakakis
137 b344501e Christos Stavrakakis
    try:
138 b344501e Christos Stavrakakis
        instance = backend_client.GetInstance(instance_name)
139 b344501e Christos Stavrakakis
    except GanetiApiError as e:
140 b344501e Christos Stavrakakis
        if e.code == 404:
141 9621c777 Christos Stavrakakis
            raise CommandError("Instance %s does not exist in backend %s"
142 9621c777 Christos Stavrakakis
                               % (instance_name, backend))
143 b344501e Christos Stavrakakis
        else:
144 b344501e Christos Stavrakakis
            raise CommandError("Unexpected error" + str(e))
145 b344501e Christos Stavrakakis
146 b344501e Christos Stavrakakis
    if new_public_nic:
147 b344501e Christos Stavrakakis
        remove_instance_nics(instance, backend_client,
148 b344501e Christos Stavrakakis
                             stream=stream)
149 660b9f3b Christos Stavrakakis
        try:
150 660b9f3b Christos Stavrakakis
            (network, address) = allocate_public_address(backend)
151 660b9f3b Christos Stavrakakis
        except Exception as e:
152 660b9f3b Christos Stavrakakis
            raise CommandError(e)
153 b344501e Christos Stavrakakis
        nic = {'ip': address, 'network': network.backend_id}
154 b344501e Christos Stavrakakis
        add_public_nic(instance_name, nic, backend_client,
155 b344501e Christos Stavrakakis
                       stream=stream)
156 b344501e Christos Stavrakakis
    else:
157 b344501e Christos Stavrakakis
        check_instance_nics(instance)
158 b344501e Christos Stavrakakis
159 b344501e Christos Stavrakakis
    shutdown_instance(instance, backend_client, stream=stream)
160 b344501e Christos Stavrakakis
161 b344501e Christos Stavrakakis
    # Create the VM in DB
162 b344501e Christos Stavrakakis
    stream.write("Creating VM entry in DB\n")
163 b344501e Christos Stavrakakis
    vm = VirtualMachine.objects.create(name=instance_name,
164 b344501e Christos Stavrakakis
                                       backend=backend,
165 b344501e Christos Stavrakakis
                                       userid=user_id,
166 b344501e Christos Stavrakakis
                                       imageid=image_id,
167 b344501e Christos Stavrakakis
                                       flavor=flavor)
168 b344501e Christos Stavrakakis
169 c34b2908 Christos Stavrakakis
    quotas.issue_and_accept_commission(vm)
170 b344501e Christos Stavrakakis
    # Rename instance
171 b344501e Christos Stavrakakis
    rename_instance(instance_name, vm.backend_vm_id, backend_client,
172 b344501e Christos Stavrakakis
                    stream)
173 b344501e Christos Stavrakakis
    # Startup instance
174 b344501e Christos Stavrakakis
    startup_instance(vm.backend_vm_id, backend_client, stream=stream)
175 b344501e Christos Stavrakakis
176 b344501e Christos Stavrakakis
    backend.put_client(backend_client)
177 b344501e Christos Stavrakakis
    return
178 b344501e Christos Stavrakakis
179 b344501e Christos Stavrakakis
180 b344501e Christos Stavrakakis
def flavor_from_instance(instance, flavor, stream=sys.stdout):
181 b344501e Christos Stavrakakis
    beparams = instance['beparams']
182 b344501e Christos Stavrakakis
    disk_sizes = instance['disk.sizes']
183 b344501e Christos Stavrakakis
    if len(disk_sizes) != 1:
184 b344501e Christos Stavrakakis
        stream.write("Instance has more than one disk.\n")
185 b344501e Christos Stavrakakis
186 b344501e Christos Stavrakakis
    disk = disk_sizes[0]
187 b344501e Christos Stavrakakis
    disk_template = instance['disk_template']
188 b344501e Christos Stavrakakis
    cpu = beparams['vcpus']
189 b344501e Christos Stavrakakis
    ram = beparams['memory']
190 b344501e Christos Stavrakakis
191 b344501e Christos Stavrakakis
    return Flavor.objects.get_or_create(disk=disk, disk_template=disk_template,
192 b344501e Christos Stavrakakis
                                        cpu=cpu, ram=ram)
193 b344501e Christos Stavrakakis
194 b344501e Christos Stavrakakis
195 b344501e Christos Stavrakakis
def check_instance_nics(instance):
196 b344501e Christos Stavrakakis
    instance_name = instance['name']
197 b344501e Christos Stavrakakis
    networks = instance['nic.networks']
198 b344501e Christos Stavrakakis
    try:
199 b344501e Christos Stavrakakis
        networks = map(id_from_network_name, networks)
200 b344501e Christos Stavrakakis
    except Network.InvalidBackendIdError:
201 b344501e Christos Stavrakakis
        raise CommandError("Instance %s has NICs that do not belong to a"
202 9621c777 Christos Stavrakakis
                           " network belonging to synnefo. Either manually"
203 9621c777 Christos Stavrakakis
                           " modify the instance NICs or specify --new-nics"
204 9621c777 Christos Stavrakakis
                           " to clear the old NICs and create a new NIC to"
205 9621c777 Christos Stavrakakis
                           " a public network of synnefo." % instance_name)
206 b344501e Christos Stavrakakis
207 b344501e Christos Stavrakakis
208 b344501e Christos Stavrakakis
def remove_instance_nics(instance, backend_client, stream=sys.stdout):
209 b344501e Christos Stavrakakis
    instance_name = instance['name']
210 b344501e Christos Stavrakakis
    ips = instance['nic.ips']
211 b344501e Christos Stavrakakis
    nic_indexes = xrange(0, len(ips))
212 b344501e Christos Stavrakakis
    op = map(lambda x: ('remove', x, {}), nic_indexes)
213 b344501e Christos Stavrakakis
    stream.write("Removing instance nics\n")
214 b344501e Christos Stavrakakis
    op.reverse()
215 b344501e Christos Stavrakakis
    jobid = backend_client.ModifyInstance(instance_name, nics=op)
216 b344501e Christos Stavrakakis
    (status, error) = wait_for_job(backend_client, jobid)
217 b344501e Christos Stavrakakis
    if status != 'success':
218 8d5795b4 Christos Stavrakakis
        raise CommandError("Cannot rename instance: %s" % error)
219 b344501e Christos Stavrakakis
220 b344501e Christos Stavrakakis
221 b344501e Christos Stavrakakis
def add_public_nic(instance_name, nic, backend_client, stream=sys.stdout):
222 b344501e Christos Stavrakakis
    stream.write("Adding public NIC %s\n" % nic)
223 b344501e Christos Stavrakakis
    jobid = backend_client.ModifyInstance(instance_name, nics=[('add', nic)])
224 b344501e Christos Stavrakakis
    (status, error) = wait_for_job(backend_client, jobid)
225 b344501e Christos Stavrakakis
    if status != 'success':
226 8d5795b4 Christos Stavrakakis
        raise CommandError("Cannot rename instance: %s" % error)
227 b344501e Christos Stavrakakis
228 b344501e Christos Stavrakakis
229 b344501e Christos Stavrakakis
def shutdown_instance(instance, backend_client, stream=sys.stdout):
230 b344501e Christos Stavrakakis
    instance_name = instance['name']
231 b344501e Christos Stavrakakis
    if instance['status'] != 'ADMIN_down':
232 b344501e Christos Stavrakakis
        stream.write("Instance is not down. Shutting down"
233 9621c777 Christos Stavrakakis
                     " instance\n")
234 b344501e Christos Stavrakakis
        jobid = backend_client.ShutdownInstance(instance_name)
235 b344501e Christos Stavrakakis
        (status, error) = wait_for_job(backend_client, jobid)
236 b344501e Christos Stavrakakis
        if status != 'success':
237 8d5795b4 Christos Stavrakakis
            raise CommandError("Cannot shutdown instance: %s" % error)
238 b344501e Christos Stavrakakis
239 b344501e Christos Stavrakakis
240 b344501e Christos Stavrakakis
def rename_instance(old_name, new_name, backend_client, stream=sys.stdout):
241 b344501e Christos Stavrakakis
    stream.write("Renaming instance to %s\n" % new_name)
242 b344501e Christos Stavrakakis
243 b344501e Christos Stavrakakis
    jobid = backend_client.RenameInstance(old_name, new_name,
244 9621c777 Christos Stavrakakis
                                          ip_check=False, name_check=False)
245 b344501e Christos Stavrakakis
    (status, error) = wait_for_job(backend_client, jobid)
246 b344501e Christos Stavrakakis
    if status != 'success':
247 8d5795b4 Christos Stavrakakis
        raise CommandError("Cannot rename instance: %s" % error)
248 b344501e Christos Stavrakakis
249 b344501e Christos Stavrakakis
250 b344501e Christos Stavrakakis
def startup_instance(name, backend_client, stream=sys.stdout):
251 b344501e Christos Stavrakakis
    stream.write("Starting instance %s\n" % name)
252 b344501e Christos Stavrakakis
    jobid = backend_client.StartupInstance(name)
253 b344501e Christos Stavrakakis
    (status, error) = wait_for_job(backend_client, jobid)
254 b344501e Christos Stavrakakis
    if status != 'success':
255 8d5795b4 Christos Stavrakakis
        raise CommandError("Cannot rename instance: %s" % error)