Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(141)

Side by Side Diff: third_party/boto/mashups/server.py

Issue 12633019: Added boto/ to depot_tools/third_party (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/depot_tools
Patch Set: Moved boto down by one Created 7 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « third_party/boto/mashups/order.py ('k') | third_party/boto/plugin.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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 """
23 High-level abstraction of an EC2 server
24 """
25 import boto
26 import boto.utils
27 from boto.mashups.iobject import IObject
28 from boto.pyami.config import Config, BotoConfigPath
29 from boto.mashups.interactive import interactive_shell
30 from boto.sdb.db.model import Model
31 from boto.sdb.db.property import StringProperty
32 import os
33 import StringIO
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 Model.__init__(self, 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="defa ult")
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_instances([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 se rver')
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_sec ret_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.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 == 'c 1.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=No ne, 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.inst ance_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.inst ance_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!'
OLDNEW
« no previous file with comments | « third_party/boto/mashups/order.py ('k') | third_party/boto/plugin.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698