root / snf-cyclades-app / synnefo / api / management / commands / server-import.py @ c34b2908
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 | 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 | b344501e | Christos Stavrakakis | (network, address) = allocate_public_address(backend) |
150 | b344501e | Christos Stavrakakis | if address is None: |
151 | 9621c777 | Christos Stavrakakis | raise CommandError("Can not allocate a public address." |
152 | b344501e | Christos Stavrakakis | " No available public network.")
|
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 | b344501e | Christos Stavrakakis | raise CommandError("Can not 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 | b344501e | Christos Stavrakakis | raise CommandError("Can not 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 | b344501e | Christos Stavrakakis | raise CommandError("Can not 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 | b344501e | Christos Stavrakakis | raise CommandError("Can not 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 | b344501e | Christos Stavrakakis | raise CommandError("Can not rename instance: %s" % error) |