Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (9.6 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 8c911970 Christos Stavrakakis
from django.core.management.base import CommandError
37 8c911970 Christos Stavrakakis
from snf_django.management.commands import SynnefoCommand
38 b344501e Christos Stavrakakis
from synnefo.management import common
39 b344501e Christos Stavrakakis
40 b344501e Christos Stavrakakis
from synnefo.db.models import VirtualMachine, Network, Flavor
41 b344501e Christos Stavrakakis
from synnefo.logic.utils import id_from_network_name, id_from_instance_name
42 c2e41963 Christos Stavrakakis
from synnefo.logic.backend import wait_for_job, connect_to_network
43 b344501e Christos Stavrakakis
from synnefo.logic.rapi import GanetiApiError
44 c2e41963 Christos Stavrakakis
from synnefo.logic import servers
45 c34b2908 Christos Stavrakakis
from synnefo import quotas
46 b344501e Christos Stavrakakis
47 b344501e Christos Stavrakakis
import sys
48 b344501e Christos Stavrakakis
49 b344501e Christos Stavrakakis
50 b344501e Christos Stavrakakis
HELP_MSG = """
51 b344501e Christos Stavrakakis

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

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

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