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) |