comparison env/lib/python3.9/site-packages/boto/mashups/server.py @ 0:4f3585e2f14b draft default tip

"planemo upload commit 60cee0fc7c0cda8592644e1aad72851dec82c959"
author shellac
date Mon, 22 Mar 2021 18:12:50 +0000
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:4f3585e2f14b
1 # Copyright (c) 2006,2007 Mitch Garnaat http://garnaat.org/
2 #
3 # Permission is hereby granted, free of charge, to any person obtaining a
4 # copy of this software and associated documentation files (the
5 # "Software"), to deal in the Software without restriction, including
6 # without limitation the rights to use, copy, modify, merge, publish, dis-
7 # tribute, sublicense, and/or sell copies of the Software, and to permit
8 # persons to whom the Software is furnished to do so, subject to the fol-
9 # lowing conditions:
10 #
11 # The above copyright notice and this permission notice shall be included
12 # in all copies or substantial portions of the Software.
13 #
14 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15 # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
16 # ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
17 # SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
18 # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20 # IN THE SOFTWARE.
21 """
22 High-level abstraction of an EC2 server
23 """
24
25 import boto
26 import boto.utils
27 from boto.compat import StringIO
28 from boto.mashups.iobject import IObject
29 from boto.pyami.config import Config, BotoConfigPath
30 from boto.mashups.interactive import interactive_shell
31 from boto.sdb.db.model import Model
32 from boto.sdb.db.property import StringProperty
33 import os
34
35
36 class ServerSet(list):
37
38 def __getattr__(self, name):
39 results = []
40 is_callable = False
41 for server in self:
42 try:
43 val = getattr(server, name)
44 if callable(val):
45 is_callable = True
46 results.append(val)
47 except:
48 results.append(None)
49 if is_callable:
50 self.map_list = results
51 return self.map
52 return results
53
54 def map(self, *args):
55 results = []
56 for fn in self.map_list:
57 results.append(fn(*args))
58 return results
59
60 class Server(Model):
61
62 @property
63 def ec2(self):
64 if self._ec2 is None:
65 self._ec2 = boto.connect_ec2()
66 return self._ec2
67
68 @classmethod
69 def Inventory(cls):
70 """
71 Returns a list of Server instances, one for each Server object
72 persisted in the db
73 """
74 l = ServerSet()
75 rs = cls.find()
76 for server in rs:
77 l.append(server)
78 return l
79
80 @classmethod
81 def Register(cls, name, instance_id, description=''):
82 s = cls()
83 s.name = name
84 s.instance_id = instance_id
85 s.description = description
86 s.save()
87 return s
88
89 def __init__(self, id=None, **kw):
90 super(Server, self).__init__(id, **kw)
91 self._reservation = None
92 self._instance = None
93 self._ssh_client = None
94 self._pkey = None
95 self._config = None
96 self._ec2 = None
97
98 name = StringProperty(unique=True, verbose_name="Name")
99 instance_id = StringProperty(verbose_name="Instance ID")
100 config_uri = StringProperty()
101 ami_id = StringProperty(verbose_name="AMI ID")
102 zone = StringProperty(verbose_name="Availability Zone")
103 security_group = StringProperty(verbose_name="Security Group", default="default")
104 key_name = StringProperty(verbose_name="Key Name")
105 elastic_ip = StringProperty(verbose_name="Elastic IP")
106 instance_type = StringProperty(verbose_name="Instance Type")
107 description = StringProperty(verbose_name="Description")
108 log = StringProperty()
109
110 def setReadOnly(self, value):
111 raise AttributeError
112
113 def getInstance(self):
114 if not self._instance:
115 if self.instance_id:
116 try:
117 rs = self.ec2.get_all_reservations([self.instance_id])
118 except:
119 return None
120 if len(rs) > 0:
121 self._reservation = rs[0]
122 self._instance = self._reservation.instances[0]
123 return self._instance
124
125 instance = property(getInstance, setReadOnly, None, 'The Instance for the server')
126
127 def getAMI(self):
128 if self.instance:
129 return self.instance.image_id
130
131 ami = property(getAMI, setReadOnly, None, 'The AMI for the server')
132
133 def getStatus(self):
134 if self.instance:
135 self.instance.update()
136 return self.instance.state
137
138 status = property(getStatus, setReadOnly, None,
139 'The status of the server')
140
141 def getHostname(self):
142 if self.instance:
143 return self.instance.public_dns_name
144
145 hostname = property(getHostname, setReadOnly, None,
146 'The public DNS name of the server')
147
148 def getPrivateHostname(self):
149 if self.instance:
150 return self.instance.private_dns_name
151
152 private_hostname = property(getPrivateHostname, setReadOnly, None,
153 'The private DNS name of the server')
154
155 def getLaunchTime(self):
156 if self.instance:
157 return self.instance.launch_time
158
159 launch_time = property(getLaunchTime, setReadOnly, None,
160 'The time the Server was started')
161
162 def getConsoleOutput(self):
163 if self.instance:
164 return self.instance.get_console_output()
165
166 console_output = property(getConsoleOutput, setReadOnly, None,
167 'Retrieve the console output for server')
168
169 def getGroups(self):
170 if self._reservation:
171 return self._reservation.groups
172 else:
173 return None
174
175 groups = property(getGroups, setReadOnly, None,
176 'The Security Groups controlling access to this server')
177
178 def getConfig(self):
179 if not self._config:
180 remote_file = BotoConfigPath
181 local_file = '%s.ini' % self.instance.id
182 self.get_file(remote_file, local_file)
183 self._config = Config(local_file)
184 return self._config
185
186 def setConfig(self, config):
187 local_file = '%s.ini' % self.instance.id
188 fp = open(local_file)
189 config.write(fp)
190 fp.close()
191 self.put_file(local_file, BotoConfigPath)
192 self._config = config
193
194 config = property(getConfig, setConfig, None,
195 'The instance data for this server')
196
197 def set_config(self, config):
198 """
199 Set SDB based config
200 """
201 self._config = config
202 self._config.dump_to_sdb("botoConfigs", self.id)
203
204 def load_config(self):
205 self._config = Config(do_load=False)
206 self._config.load_from_sdb("botoConfigs", self.id)
207
208 def stop(self):
209 if self.instance:
210 self.instance.stop()
211
212 def start(self):
213 self.stop()
214 ec2 = boto.connect_ec2()
215 ami = ec2.get_all_images(image_ids = [str(self.ami_id)])[0]
216 groups = ec2.get_all_security_groups(groupnames=[str(self.security_group)])
217 if not self._config:
218 self.load_config()
219 if not self._config.has_section("Credentials"):
220 self._config.add_section("Credentials")
221 self._config.set("Credentials", "aws_access_key_id", ec2.aws_access_key_id)
222 self._config.set("Credentials", "aws_secret_access_key", ec2.aws_secret_access_key)
223
224 if not self._config.has_section("Pyami"):
225 self._config.add_section("Pyami")
226
227 if self._manager.domain:
228 self._config.set('Pyami', 'server_sdb_domain', self._manager.domain.name)
229 self._config.set("Pyami", 'server_sdb_name', self.name)
230
231 cfg = StringIO()
232 self._config.write(cfg)
233 cfg = cfg.getvalue()
234 r = ami.run(min_count=1,
235 max_count=1,
236 key_name=self.key_name,
237 security_groups = groups,
238 instance_type = self.instance_type,
239 placement = self.zone,
240 user_data = cfg)
241 i = r.instances[0]
242 self.instance_id = i.id
243 self.put()
244 if self.elastic_ip:
245 ec2.associate_address(self.instance_id, self.elastic_ip)
246
247 def reboot(self):
248 if self.instance:
249 self.instance.reboot()
250
251 def get_ssh_client(self, key_file=None, host_key_file='~/.ssh/known_hosts',
252 uname='root'):
253 import paramiko
254 if not self.instance:
255 print('No instance yet!')
256 return
257 if not self._ssh_client:
258 if not key_file:
259 iobject = IObject()
260 key_file = iobject.get_filename('Path to OpenSSH Key file')
261 self._pkey = paramiko.RSAKey.from_private_key_file(key_file)
262 self._ssh_client = paramiko.SSHClient()
263 self._ssh_client.load_system_host_keys()
264 self._ssh_client.load_host_keys(os.path.expanduser(host_key_file))
265 self._ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
266 self._ssh_client.connect(self.instance.public_dns_name,
267 username=uname, pkey=self._pkey)
268 return self._ssh_client
269
270 def get_file(self, remotepath, localpath):
271 ssh_client = self.get_ssh_client()
272 sftp_client = ssh_client.open_sftp()
273 sftp_client.get(remotepath, localpath)
274
275 def put_file(self, localpath, remotepath):
276 ssh_client = self.get_ssh_client()
277 sftp_client = ssh_client.open_sftp()
278 sftp_client.put(localpath, remotepath)
279
280 def listdir(self, remotepath):
281 ssh_client = self.get_ssh_client()
282 sftp_client = ssh_client.open_sftp()
283 return sftp_client.listdir(remotepath)
284
285 def shell(self, key_file=None):
286 ssh_client = self.get_ssh_client(key_file)
287 channel = ssh_client.invoke_shell()
288 interactive_shell(channel)
289
290 def bundle_image(self, prefix, key_file, cert_file, size):
291 print('bundling image...')
292 print('\tcopying cert and pk over to /mnt directory on server')
293 ssh_client = self.get_ssh_client()
294 sftp_client = ssh_client.open_sftp()
295 path, name = os.path.split(key_file)
296 remote_key_file = '/mnt/%s' % name
297 self.put_file(key_file, remote_key_file)
298 path, name = os.path.split(cert_file)
299 remote_cert_file = '/mnt/%s' % name
300 self.put_file(cert_file, remote_cert_file)
301 print('\tdeleting %s' % BotoConfigPath)
302 # delete the metadata.ini file if it exists
303 try:
304 sftp_client.remove(BotoConfigPath)
305 except:
306 pass
307 command = 'sudo ec2-bundle-vol '
308 command += '-c %s -k %s ' % (remote_cert_file, remote_key_file)
309 command += '-u %s ' % self._reservation.owner_id
310 command += '-p %s ' % prefix
311 command += '-s %d ' % size
312 command += '-d /mnt '
313 if self.instance.instance_type == 'm1.small' or self.instance_type == 'c1.medium':
314 command += '-r i386'
315 else:
316 command += '-r x86_64'
317 print('\t%s' % command)
318 t = ssh_client.exec_command(command)
319 response = t[1].read()
320 print('\t%s' % response)
321 print('\t%s' % t[2].read())
322 print('...complete!')
323
324 def upload_bundle(self, bucket, prefix):
325 print('uploading bundle...')
326 command = 'ec2-upload-bundle '
327 command += '-m /mnt/%s.manifest.xml ' % prefix
328 command += '-b %s ' % bucket
329 command += '-a %s ' % self.ec2.aws_access_key_id
330 command += '-s %s ' % self.ec2.aws_secret_access_key
331 print('\t%s' % command)
332 ssh_client = self.get_ssh_client()
333 t = ssh_client.exec_command(command)
334 response = t[1].read()
335 print('\t%s' % response)
336 print('\t%s' % t[2].read())
337 print('...complete!')
338
339 def create_image(self, bucket=None, prefix=None, key_file=None, cert_file=None, size=None):
340 iobject = IObject()
341 if not bucket:
342 bucket = iobject.get_string('Name of S3 bucket')
343 if not prefix:
344 prefix = iobject.get_string('Prefix for AMI file')
345 if not key_file:
346 key_file = iobject.get_filename('Path to RSA private key file')
347 if not cert_file:
348 cert_file = iobject.get_filename('Path to RSA public cert file')
349 if not size:
350 size = iobject.get_int('Size (in MB) of bundled image')
351 self.bundle_image(prefix, key_file, cert_file, size)
352 self.upload_bundle(bucket, prefix)
353 print('registering image...')
354 self.image_id = self.ec2.register_image('%s/%s.manifest.xml' % (bucket, prefix))
355 return self.image_id
356
357 def attach_volume(self, volume, device="/dev/sdp"):
358 """
359 Attach an EBS volume to this server
360
361 :param volume: EBS Volume to attach
362 :type volume: boto.ec2.volume.Volume
363
364 :param device: Device to attach to (default to /dev/sdp)
365 :type device: string
366 """
367 if hasattr(volume, "id"):
368 volume_id = volume.id
369 else:
370 volume_id = volume
371 return self.ec2.attach_volume(volume_id=volume_id, instance_id=self.instance_id, device=device)
372
373 def detach_volume(self, volume):
374 """
375 Detach an EBS volume from this server
376
377 :param volume: EBS Volume to detach
378 :type volume: boto.ec2.volume.Volume
379 """
380 if hasattr(volume, "id"):
381 volume_id = volume.id
382 else:
383 volume_id = volume
384 return self.ec2.detach_volume(volume_id=volume_id, instance_id=self.instance_id)
385
386 def install_package(self, package_name):
387 print('installing %s...' % package_name)
388 command = 'yum -y install %s' % package_name
389 print('\t%s' % command)
390 ssh_client = self.get_ssh_client()
391 t = ssh_client.exec_command(command)
392 response = t[1].read()
393 print('\t%s' % response)
394 print('\t%s' % t[2].read())
395 print('...complete!')