Statistics
| Branch: | Tag: | Revision:

root / snf-cyclades-app / synnefo / logic / management / commands / server-import.py @ a6e6fe48

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 c2e41963 Christos Stavrakakis
from synnefo.logic.backend import wait_for_job, connect_to_network
42 b344501e Christos Stavrakakis
from synnefo.logic.rapi import GanetiApiError
43 c2e41963 Christos Stavrakakis
from synnefo.logic import servers
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 a6e6fe48 Christos Stavrakakis
    flavor = common.get_resource("flavor", flavor_id)
133 a6e6fe48 Christos Stavrakakis
    backend = common.get_resource("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 c2e41963 Christos Stavrakakis
    if not new_public_nic:
147 b344501e Christos Stavrakakis
        check_instance_nics(instance)
148 b344501e Christos Stavrakakis
149 b344501e Christos Stavrakakis
    shutdown_instance(instance, backend_client, stream=stream)
150 b344501e Christos Stavrakakis
151 b344501e Christos Stavrakakis
    # Create the VM in DB
152 b344501e Christos Stavrakakis
    stream.write("Creating VM entry in DB\n")
153 b344501e Christos Stavrakakis
    vm = VirtualMachine.objects.create(name=instance_name,
154 b344501e Christos Stavrakakis
                                       backend=backend,
155 b344501e Christos Stavrakakis
                                       userid=user_id,
156 b344501e Christos Stavrakakis
                                       imageid=image_id,
157 b344501e Christos Stavrakakis
                                       flavor=flavor)
158 b344501e Christos Stavrakakis
159 c34b2908 Christos Stavrakakis
    quotas.issue_and_accept_commission(vm)
160 c2e41963 Christos Stavrakakis
161 c2e41963 Christos Stavrakakis
    if new_public_nic:
162 c2e41963 Christos Stavrakakis
        remove_instance_nics(instance, backend_client,
163 c2e41963 Christos Stavrakakis
                             stream=stream)
164 c2e41963 Christos Stavrakakis
165 b344501e Christos Stavrakakis
    # Rename instance
166 b344501e Christos Stavrakakis
    rename_instance(instance_name, vm.backend_vm_id, backend_client,
167 b344501e Christos Stavrakakis
                    stream)
168 c2e41963 Christos Stavrakakis
169 c2e41963 Christos Stavrakakis
    if new_public_nic:
170 c2e41963 Christos Stavrakakis
        ports = servers.create_instance_ports(user_id)
171 c2e41963 Christos Stavrakakis
        stream.write("Adding new NICs to server")
172 c2e41963 Christos Stavrakakis
        [servers.associate_port_with_machine(port, vm)
173 c2e41963 Christos Stavrakakis
         for port in ports]
174 c2e41963 Christos Stavrakakis
        [connect_to_network(vm, port) for port in ports]
175 c2e41963 Christos Stavrakakis
176 b344501e Christos Stavrakakis
    # Startup instance
177 b344501e Christos Stavrakakis
    startup_instance(vm.backend_vm_id, backend_client, stream=stream)
178 b344501e Christos Stavrakakis
179 b344501e Christos Stavrakakis
    backend.put_client(backend_client)
180 b344501e Christos Stavrakakis
    return
181 b344501e Christos Stavrakakis
182 b344501e Christos Stavrakakis
183 b344501e Christos Stavrakakis
def flavor_from_instance(instance, flavor, stream=sys.stdout):
184 b344501e Christos Stavrakakis
    beparams = instance['beparams']
185 b344501e Christos Stavrakakis
    disk_sizes = instance['disk.sizes']
186 b344501e Christos Stavrakakis
    if len(disk_sizes) != 1:
187 b344501e Christos Stavrakakis
        stream.write("Instance has more than one disk.\n")
188 b344501e Christos Stavrakakis
189 b344501e Christos Stavrakakis
    disk = disk_sizes[0]
190 b344501e Christos Stavrakakis
    disk_template = instance['disk_template']
191 b344501e Christos Stavrakakis
    cpu = beparams['vcpus']
192 b344501e Christos Stavrakakis
    ram = beparams['memory']
193 b344501e Christos Stavrakakis
194 b344501e Christos Stavrakakis
    return Flavor.objects.get_or_create(disk=disk, disk_template=disk_template,
195 b344501e Christos Stavrakakis
                                        cpu=cpu, ram=ram)
196 b344501e Christos Stavrakakis
197 b344501e Christos Stavrakakis
198 b344501e Christos Stavrakakis
def check_instance_nics(instance):
199 b344501e Christos Stavrakakis
    instance_name = instance['name']
200 c2e41963 Christos Stavrakakis
    networks = instance['nic.networks.names']
201 c2e41963 Christos Stavrakakis
    print networks
202 b344501e Christos Stavrakakis
    try:
203 b344501e Christos Stavrakakis
        networks = map(id_from_network_name, networks)
204 b344501e Christos Stavrakakis
    except Network.InvalidBackendIdError:
205 b344501e Christos Stavrakakis
        raise CommandError("Instance %s has NICs that do not belong to a"
206 9621c777 Christos Stavrakakis
                           " network belonging to synnefo. Either manually"
207 9621c777 Christos Stavrakakis
                           " modify the instance NICs or specify --new-nics"
208 9621c777 Christos Stavrakakis
                           " to clear the old NICs and create a new NIC to"
209 9621c777 Christos Stavrakakis
                           " a public network of synnefo." % instance_name)
210 b344501e Christos Stavrakakis
211 b344501e Christos Stavrakakis
212 b344501e Christos Stavrakakis
def remove_instance_nics(instance, backend_client, stream=sys.stdout):
213 b344501e Christos Stavrakakis
    instance_name = instance['name']
214 b344501e Christos Stavrakakis
    ips = instance['nic.ips']
215 b344501e Christos Stavrakakis
    nic_indexes = xrange(0, len(ips))
216 b344501e Christos Stavrakakis
    op = map(lambda x: ('remove', x, {}), nic_indexes)
217 b344501e Christos Stavrakakis
    stream.write("Removing instance nics\n")
218 b344501e Christos Stavrakakis
    op.reverse()
219 b344501e Christos Stavrakakis
    jobid = backend_client.ModifyInstance(instance_name, nics=op)
220 b344501e Christos Stavrakakis
    (status, error) = wait_for_job(backend_client, jobid)
221 b344501e Christos Stavrakakis
    if status != 'success':
222 c2e41963 Christos Stavrakakis
        raise CommandError("Cannot remove instance NICs: %s" % error)
223 b344501e Christos Stavrakakis
224 b344501e Christos Stavrakakis
225 b344501e Christos Stavrakakis
def add_public_nic(instance_name, nic, backend_client, stream=sys.stdout):
226 b344501e Christos Stavrakakis
    stream.write("Adding public NIC %s\n" % nic)
227 b344501e Christos Stavrakakis
    jobid = backend_client.ModifyInstance(instance_name, nics=[('add', nic)])
228 b344501e Christos Stavrakakis
    (status, error) = wait_for_job(backend_client, jobid)
229 b344501e Christos Stavrakakis
    if status != 'success':
230 8d5795b4 Christos Stavrakakis
        raise CommandError("Cannot rename instance: %s" % error)
231 b344501e Christos Stavrakakis
232 b344501e Christos Stavrakakis
233 b344501e Christos Stavrakakis
def shutdown_instance(instance, backend_client, stream=sys.stdout):
234 b344501e Christos Stavrakakis
    instance_name = instance['name']
235 b344501e Christos Stavrakakis
    if instance['status'] != 'ADMIN_down':
236 c2e41963 Christos Stavrakakis
        stream.write("Instance is not down. Shutting down instance...\n")
237 b344501e Christos Stavrakakis
        jobid = backend_client.ShutdownInstance(instance_name)
238 b344501e Christos Stavrakakis
        (status, error) = wait_for_job(backend_client, jobid)
239 b344501e Christos Stavrakakis
        if status != 'success':
240 8d5795b4 Christos Stavrakakis
            raise CommandError("Cannot shutdown instance: %s" % error)
241 b344501e Christos Stavrakakis
242 b344501e Christos Stavrakakis
243 b344501e Christos Stavrakakis
def rename_instance(old_name, new_name, backend_client, stream=sys.stdout):
244 b344501e Christos Stavrakakis
    stream.write("Renaming instance to %s\n" % new_name)
245 b344501e Christos Stavrakakis
246 b344501e Christos Stavrakakis
    jobid = backend_client.RenameInstance(old_name, new_name,
247 9621c777 Christos Stavrakakis
                                          ip_check=False, name_check=False)
248 b344501e Christos Stavrakakis
    (status, error) = wait_for_job(backend_client, jobid)
249 b344501e Christos Stavrakakis
    if status != 'success':
250 8d5795b4 Christos Stavrakakis
        raise CommandError("Cannot rename instance: %s" % error)
251 b344501e Christos Stavrakakis
252 b344501e Christos Stavrakakis
253 b344501e Christos Stavrakakis
def startup_instance(name, backend_client, stream=sys.stdout):
254 b344501e Christos Stavrakakis
    stream.write("Starting instance %s\n" % name)
255 b344501e Christos Stavrakakis
    jobid = backend_client.StartupInstance(name)
256 b344501e Christos Stavrakakis
    (status, error) = wait_for_job(backend_client, jobid)
257 b344501e Christos Stavrakakis
    if status != 'success':
258 8d5795b4 Christos Stavrakakis
        raise CommandError("Cannot rename instance: %s" % error)