gcloud 0.0.2 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data.tar.gz.sig +2 -3
- data/CHANGELOG +4 -0
- data/LICENSE +674 -0
- data/Manifest +111 -0
- data/README.md +4 -3
- data/bin/gcutil +53 -0
- data/gcloud.gemspec +4 -3
- data/packages/gcutil-1.7.1/CHANGELOG +197 -0
- data/packages/gcutil-1.7.1/LICENSE +202 -0
- data/packages/gcutil-1.7.1/VERSION +1 -0
- data/packages/gcutil-1.7.1/gcutil +53 -0
- data/packages/gcutil-1.7.1/lib/google_api_python_client/LICENSE +23 -0
- data/packages/gcutil-1.7.1/lib/google_api_python_client/apiclient/__init__.py +1 -0
- data/packages/gcutil-1.7.1/lib/google_api_python_client/apiclient/discovery.py +743 -0
- data/packages/gcutil-1.7.1/lib/google_api_python_client/apiclient/errors.py +123 -0
- data/packages/gcutil-1.7.1/lib/google_api_python_client/apiclient/ext/__init__.py +0 -0
- data/packages/gcutil-1.7.1/lib/google_api_python_client/apiclient/http.py +1443 -0
- data/packages/gcutil-1.7.1/lib/google_api_python_client/apiclient/mimeparse.py +172 -0
- data/packages/gcutil-1.7.1/lib/google_api_python_client/apiclient/model.py +385 -0
- data/packages/gcutil-1.7.1/lib/google_api_python_client/apiclient/schema.py +303 -0
- data/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/__init__.py +1 -0
- data/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/anyjson.py +32 -0
- data/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/appengine.py +528 -0
- data/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/client.py +1139 -0
- data/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/clientsecrets.py +105 -0
- data/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/crypt.py +244 -0
- data/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/django_orm.py +124 -0
- data/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/file.py +107 -0
- data/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/locked_file.py +343 -0
- data/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/multistore_file.py +379 -0
- data/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/tools.py +174 -0
- data/packages/gcutil-1.7.1/lib/google_api_python_client/uritemplate/__init__.py +147 -0
- data/packages/gcutil-1.7.1/lib/google_apputils/LICENSE +202 -0
- data/packages/gcutil-1.7.1/lib/google_apputils/google/__init__.py +3 -0
- data/packages/gcutil-1.7.1/lib/google_apputils/google/apputils/__init__.py +3 -0
- data/packages/gcutil-1.7.1/lib/google_apputils/google/apputils/app.py +356 -0
- data/packages/gcutil-1.7.1/lib/google_apputils/google/apputils/appcommands.py +783 -0
- data/packages/gcutil-1.7.1/lib/google_apputils/google/apputils/basetest.py +1260 -0
- data/packages/gcutil-1.7.1/lib/google_apputils/google/apputils/datelib.py +421 -0
- data/packages/gcutil-1.7.1/lib/google_apputils/google/apputils/debug.py +60 -0
- data/packages/gcutil-1.7.1/lib/google_apputils/google/apputils/file_util.py +181 -0
- data/packages/gcutil-1.7.1/lib/google_apputils/google/apputils/resources.py +67 -0
- data/packages/gcutil-1.7.1/lib/google_apputils/google/apputils/run_script_module.py +217 -0
- data/packages/gcutil-1.7.1/lib/google_apputils/google/apputils/setup_command.py +159 -0
- data/packages/gcutil-1.7.1/lib/google_apputils/google/apputils/shellutil.py +49 -0
- data/packages/gcutil-1.7.1/lib/google_apputils/google/apputils/stopwatch.py +204 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/__init__.py +0 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/auth_helper.py +140 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/auth_helper_test.py +149 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/auto_auth.py +130 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/auto_auth_test.py +75 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/basic_cmds.py +128 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/basic_cmds_test.py +111 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/command_base.py +1808 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/command_base_test.py +1651 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/compute/v1beta13.json +2851 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/compute/v1beta14.json +3361 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/disk_cmds.py +342 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/disk_cmds_test.py +474 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/firewall_cmds.py +344 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/firewall_cmds_test.py +231 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/flags_cache.py +274 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/gcutil +89 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/gcutil_logging.py +69 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/image_cmds.py +262 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/image_cmds_test.py +172 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/instance_cmds.py +1506 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/instance_cmds_test.py +1904 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/kernel_cmds.py +91 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/kernel_cmds_test.py +56 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/machine_type_cmds.py +106 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/machine_type_cmds_test.py +59 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/metadata.py +96 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/metadata_lib.py +357 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/metadata_test.py +84 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/mock_api.py +420 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/mock_metadata.py +58 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/move_cmds.py +824 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/move_cmds_test.py +307 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/network_cmds.py +178 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/network_cmds_test.py +133 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/operation_cmds.py +181 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/operation_cmds_test.py +196 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/path_initializer.py +38 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/project_cmds.py +173 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/project_cmds_test.py +111 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/scopes.py +61 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/scopes_test.py +50 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/snapshot_cmds.py +276 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/snapshot_cmds_test.py +260 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/ssh_keys.py +266 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/ssh_keys_test.py +128 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/table_formatter.py +563 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/thread_pool.py +188 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/thread_pool_test.py +88 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/utils.py +208 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/utils_test.py +193 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/version.py +17 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/version_checker.py +246 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/version_checker_test.py +271 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/zone_cmds.py +151 -0
- data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/zone_cmds_test.py +60 -0
- data/packages/gcutil-1.7.1/lib/httplib2/LICENSE +21 -0
- data/packages/gcutil-1.7.1/lib/httplib2/httplib2/__init__.py +1630 -0
- data/packages/gcutil-1.7.1/lib/httplib2/httplib2/cacerts.txt +714 -0
- data/packages/gcutil-1.7.1/lib/httplib2/httplib2/iri2uri.py +110 -0
- data/packages/gcutil-1.7.1/lib/httplib2/httplib2/socks.py +438 -0
- data/packages/gcutil-1.7.1/lib/iso8601/LICENSE +20 -0
- data/packages/gcutil-1.7.1/lib/iso8601/iso8601/__init__.py +1 -0
- data/packages/gcutil-1.7.1/lib/iso8601/iso8601/iso8601.py +102 -0
- data/packages/gcutil-1.7.1/lib/iso8601/iso8601/test_iso8601.py +111 -0
- data/packages/gcutil-1.7.1/lib/python_gflags/AUTHORS +2 -0
- data/packages/gcutil-1.7.1/lib/python_gflags/LICENSE +28 -0
- data/packages/gcutil-1.7.1/lib/python_gflags/gflags.py +2862 -0
- data/packages/gcutil-1.7.1/lib/python_gflags/gflags2man.py +544 -0
- data/packages/gcutil-1.7.1/lib/python_gflags/gflags_validators.py +187 -0
- metadata +118 -5
- metadata.gz.sig +0 -0
@@ -0,0 +1,188 @@
|
|
1
|
+
# Copyright 2012 Google Inc. All Rights Reserved.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
"""A simple thread pool class for doing multiple concurrent API operations."""
|
16
|
+
|
17
|
+
|
18
|
+
|
19
|
+
import logging
|
20
|
+
import Queue
|
21
|
+
import sys
|
22
|
+
import threading
|
23
|
+
import time
|
24
|
+
import traceback
|
25
|
+
|
26
|
+
LOGGER = logging.getLogger('gcutil-logs')
|
27
|
+
|
28
|
+
|
29
|
+
class ThreadPoolError(Exception):
|
30
|
+
"""An error occurred in this module."""
|
31
|
+
pass
|
32
|
+
|
33
|
+
|
34
|
+
class Operation(object):
|
35
|
+
"""An operation to be executed by the threadpool.
|
36
|
+
|
37
|
+
Override this and implement the Run() method.
|
38
|
+
"""
|
39
|
+
|
40
|
+
def __init__(self):
|
41
|
+
"""Initializer."""
|
42
|
+
self._result = None
|
43
|
+
self._raised_exception = False
|
44
|
+
|
45
|
+
def Run(self):
|
46
|
+
"""Override this method to execute this operation."""
|
47
|
+
raise NotImplementedError('pure virtual method called')
|
48
|
+
|
49
|
+
def _DoOperation(self):
|
50
|
+
"""Internal runner that captures result."""
|
51
|
+
try:
|
52
|
+
self._result = self.Run()
|
53
|
+
except: # pylint: disable-msg=W0702
|
54
|
+
self._raised_exception = True
|
55
|
+
a = sys.exc_info()
|
56
|
+
# If a string was thrown, a[1] is None. In Python 2.5, if an exception
|
57
|
+
# was thrown without a message, a is a 1-tuple. Otherwise, the exception
|
58
|
+
# object is in a[1].
|
59
|
+
if len(a) < 2 or a[1] is None:
|
60
|
+
self._result = a[0]
|
61
|
+
else:
|
62
|
+
self._result = a[1]
|
63
|
+
LOGGER.debug(traceback.format_exc())
|
64
|
+
|
65
|
+
def Result(self):
|
66
|
+
"""Get the operation's result.
|
67
|
+
|
68
|
+
If the operation is incomplete the return value will be None. If the
|
69
|
+
operation raised an exception, the return value will be the exception
|
70
|
+
object.
|
71
|
+
|
72
|
+
Returns:
|
73
|
+
The operation's result.
|
74
|
+
"""
|
75
|
+
return self._result
|
76
|
+
|
77
|
+
def RaisedException(self):
|
78
|
+
"""Did the operation raise an exception?
|
79
|
+
|
80
|
+
Will be False if the operation has not yet completed.
|
81
|
+
|
82
|
+
Returns:
|
83
|
+
True if an exception was raised by the operation.
|
84
|
+
"""
|
85
|
+
return self._raised_exception
|
86
|
+
|
87
|
+
|
88
|
+
class Worker(threading.Thread):
|
89
|
+
"""Thread executing tasks from a given tasks queue."""
|
90
|
+
|
91
|
+
def __init__(self, queue):
|
92
|
+
threading.Thread.__init__(self)
|
93
|
+
self._queue = queue
|
94
|
+
self.daemon = True
|
95
|
+
self.start()
|
96
|
+
|
97
|
+
def run(self):
|
98
|
+
# pylint: disable-msg=W0212
|
99
|
+
while True:
|
100
|
+
op = self._queue.get()
|
101
|
+
if op is None:
|
102
|
+
self._queue.task_done()
|
103
|
+
break
|
104
|
+
op._DoOperation() # pylint: disable-msg=W0212
|
105
|
+
self._queue.task_done()
|
106
|
+
|
107
|
+
|
108
|
+
class ThreadPool(object):
|
109
|
+
"""Pool of threads consuming tasks from a queue.
|
110
|
+
|
111
|
+
Note that operations on the thread pool itself (submitting, waiting,
|
112
|
+
shutdown) are not, themselves, multithread safe.
|
113
|
+
"""
|
114
|
+
|
115
|
+
# States
|
116
|
+
_NOT_RUNNING = 0
|
117
|
+
_RUNNING = 1
|
118
|
+
_TERMINATING = 3
|
119
|
+
_TERMINATED = 4
|
120
|
+
|
121
|
+
def __init__(self, num_threads):
|
122
|
+
self._queue = Queue.Queue()
|
123
|
+
self._num_threads = num_threads
|
124
|
+
self._state = self._NOT_RUNNING
|
125
|
+
|
126
|
+
self._workers = []
|
127
|
+
for _ in range(num_threads):
|
128
|
+
self._workers.append(Worker(self._queue))
|
129
|
+
self._state = self._RUNNING
|
130
|
+
|
131
|
+
def __del__(self):
|
132
|
+
# Shut down everything so that we don't leak memory.
|
133
|
+
if self._state == self._RUNNING:
|
134
|
+
self.WaitShutdown()
|
135
|
+
|
136
|
+
def Add(self, op):
|
137
|
+
"""Add an operation to the queue.
|
138
|
+
|
139
|
+
Note that this is not thread safe.
|
140
|
+
|
141
|
+
Args:
|
142
|
+
op: An Operation object to add to the thread pool queue
|
143
|
+
|
144
|
+
Raises:
|
145
|
+
ThreadPoolError: if not in running state.
|
146
|
+
ValueError: if op isn't an Operation object
|
147
|
+
"""
|
148
|
+
if self._state != self._RUNNING:
|
149
|
+
raise ThreadPoolError('ThreadPool not running')
|
150
|
+
if not isinstance(op, Operation):
|
151
|
+
raise ValueError('Nonoperation argument to AddOperation')
|
152
|
+
self._queue.put(op)
|
153
|
+
|
154
|
+
def _InternalWait(self):
|
155
|
+
"""Wait for all items to clear.
|
156
|
+
|
157
|
+
This will come up for air once in a while so that we can capture
|
158
|
+
keyboard interrupt. Unfortunately Queue.join() isn't
|
159
|
+
interruptable.
|
160
|
+
"""
|
161
|
+
while not self._queue.empty():
|
162
|
+
time.sleep(0.2)
|
163
|
+
|
164
|
+
def WaitAll(self):
|
165
|
+
"""Wait for completion of all the tasks in the queue.
|
166
|
+
|
167
|
+
Note that this is not thread safe.
|
168
|
+
|
169
|
+
Raises:
|
170
|
+
ThreadPoolError: if not in running state.
|
171
|
+
"""
|
172
|
+
if self._state != self._RUNNING:
|
173
|
+
raise ThreadPoolError('ThreadPool not running')
|
174
|
+
self._InternalWait()
|
175
|
+
|
176
|
+
def WaitShutdown(self):
|
177
|
+
"""Wait for completion of all tasks and shut down the ThreadPool.
|
178
|
+
|
179
|
+
Note that this is not thread safe.
|
180
|
+
"""
|
181
|
+
if self._state != self._RUNNING:
|
182
|
+
raise ThreadPoolError('ThreadPool not running')
|
183
|
+
self._state = self._TERMINATING
|
184
|
+
# Inject a set of sentinal values to have the workers exit.
|
185
|
+
for _ in range(self._num_threads):
|
186
|
+
self._queue.put(None)
|
187
|
+
self._InternalWait()
|
188
|
+
self._state = self._TERMINATED
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# Copyright 2012 Google Inc. All Rights Reserved.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
"""Tests for thread_pool."""
|
16
|
+
|
17
|
+
|
18
|
+
|
19
|
+
import path_initializer
|
20
|
+
path_initializer.InitializeSysPath()
|
21
|
+
|
22
|
+
import time
|
23
|
+
|
24
|
+
import unittest
|
25
|
+
from gcutil import thread_pool
|
26
|
+
|
27
|
+
|
28
|
+
class TestOperation(thread_pool.Operation):
|
29
|
+
def __init__(self, raise_exception=False, sleep_time=0):
|
30
|
+
thread_pool.Operation.__init__(self)
|
31
|
+
self.raise_exception = raise_exception
|
32
|
+
self.sleep_time = sleep_time
|
33
|
+
|
34
|
+
def Run(self):
|
35
|
+
if self.sleep_time:
|
36
|
+
time.sleep(self.sleep_time)
|
37
|
+
if self.raise_exception:
|
38
|
+
raise Exception('Exception!')
|
39
|
+
return 42
|
40
|
+
|
41
|
+
|
42
|
+
class ThreadPoolTest(unittest.TestCase):
|
43
|
+
def testBasic(self):
|
44
|
+
"""Test basic start up and shutdown."""
|
45
|
+
tp = thread_pool.ThreadPool(3)
|
46
|
+
tp.WaitShutdown()
|
47
|
+
|
48
|
+
def testSubmit(self):
|
49
|
+
tp = thread_pool.ThreadPool(3)
|
50
|
+
|
51
|
+
ops = []
|
52
|
+
for _ in xrange(20):
|
53
|
+
op = TestOperation()
|
54
|
+
ops.append(op)
|
55
|
+
tp.Add(op)
|
56
|
+
tp.WaitShutdown()
|
57
|
+
for op in ops:
|
58
|
+
self.assertEqual(op.Result(), 42)
|
59
|
+
self.assertFalse(op.RaisedException())
|
60
|
+
|
61
|
+
def testLongOps(self):
|
62
|
+
tp = thread_pool.ThreadPool(3)
|
63
|
+
|
64
|
+
ops = []
|
65
|
+
for _ in xrange(10):
|
66
|
+
op = TestOperation(sleep_time=0.1)
|
67
|
+
ops.append(op)
|
68
|
+
tp.Add(op)
|
69
|
+
tp.WaitShutdown()
|
70
|
+
for op in ops:
|
71
|
+
self.assertEqual(op.Result(), 42)
|
72
|
+
self.assertFalse(op.RaisedException())
|
73
|
+
|
74
|
+
def testExceptionOps(self):
|
75
|
+
tp = thread_pool.ThreadPool(3)
|
76
|
+
|
77
|
+
ops = []
|
78
|
+
for _ in xrange(20):
|
79
|
+
op = TestOperation(raise_exception=0.1)
|
80
|
+
ops.append(op)
|
81
|
+
tp.Add(op)
|
82
|
+
tp.WaitShutdown()
|
83
|
+
for op in ops:
|
84
|
+
self.assertEqual(str(op.Result()), 'Exception!')
|
85
|
+
self.assertTrue(op.RaisedException())
|
86
|
+
|
87
|
+
if __name__ == '__main__':
|
88
|
+
unittest.main()
|
@@ -0,0 +1,208 @@
|
|
1
|
+
# Copyright 2012 Google Inc. All Rights Reserved.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
"""A set of utility functions."""
|
16
|
+
|
17
|
+
import cStringIO
|
18
|
+
import numbers
|
19
|
+
import socket
|
20
|
+
import sys
|
21
|
+
|
22
|
+
|
23
|
+
|
24
|
+
|
25
|
+
def SimpleName(entity):
|
26
|
+
if entity is None:
|
27
|
+
return ''
|
28
|
+
|
29
|
+
elif isinstance(entity, basestring):
|
30
|
+
if 'projects/google/' in entity:
|
31
|
+
return 'google/' + entity.split('/')[-1]
|
32
|
+
else:
|
33
|
+
return entity.split('/')[-1]
|
34
|
+
|
35
|
+
elif isinstance(entity, numbers.Number):
|
36
|
+
return str(entity)
|
37
|
+
|
38
|
+
raise ValueError('Expected number or string: ' + str(entity))
|
39
|
+
|
40
|
+
|
41
|
+
def FlattenList(list):
|
42
|
+
"""Flattens a list of lists."""
|
43
|
+
return [item for sublist in list for item in sublist]
|
44
|
+
|
45
|
+
|
46
|
+
def RegexesToFilterExpression(regexes, op='eq'):
|
47
|
+
"""Converts a list of regular expressions to a filter expression on name.
|
48
|
+
|
49
|
+
Args:
|
50
|
+
regexes: A list of regular expressions to use for matching
|
51
|
+
resource names. Since resource names cannot contain whitespace
|
52
|
+
characters, regular expressions are split on whitespace (e.g.,
|
53
|
+
'[a-z]+ [0-9]+' will be treated as two separate regular expressions).
|
54
|
+
|
55
|
+
Returns:
|
56
|
+
The Google Compute Engine filter expression or None
|
57
|
+
if regexes evaluates to False.
|
58
|
+
"""
|
59
|
+
if not regexes:
|
60
|
+
return None
|
61
|
+
regexes = FlattenList(regex.split() for regex in regexes)
|
62
|
+
return 'name %s %s' % (op, '|'.join(regexes))
|
63
|
+
|
64
|
+
|
65
|
+
def SimplePrint(text, *args, **kwargs):
|
66
|
+
"""Prints the given text without a new-line character at the end."""
|
67
|
+
print text.format(*args, **kwargs),
|
68
|
+
sys.stdout.flush()
|
69
|
+
|
70
|
+
|
71
|
+
def ListStrings(strings, prefix=' '):
|
72
|
+
"""Returns a string containing each item in strings on its own line.
|
73
|
+
|
74
|
+
Args:
|
75
|
+
strings: The list of strings to place in the result.
|
76
|
+
prefix: A string to place before each name.
|
77
|
+
|
78
|
+
Returns:
|
79
|
+
A string containing the names.
|
80
|
+
"""
|
81
|
+
strings = sorted(strings)
|
82
|
+
buf = cStringIO.StringIO()
|
83
|
+
for string in strings:
|
84
|
+
buf.write(prefix + str(string) + '\n')
|
85
|
+
return buf.getvalue().rstrip()
|
86
|
+
|
87
|
+
|
88
|
+
def Proceed(message=None):
|
89
|
+
"""Prompts the user to proceed.
|
90
|
+
|
91
|
+
Args:
|
92
|
+
message: An optional message to include before
|
93
|
+
'Proceed? [y/N] ' is printed.
|
94
|
+
|
95
|
+
Returns:
|
96
|
+
True if the user answers yes.
|
97
|
+
"""
|
98
|
+
message = ((message or '') + ' Proceed? [y/N] ').lstrip()
|
99
|
+
return raw_input(message).strip().lower() == 'y'
|
100
|
+
|
101
|
+
|
102
|
+
def ParseProtocol(protocol_string):
|
103
|
+
"""Attempt to parse a protocol number from a string.
|
104
|
+
|
105
|
+
Args:
|
106
|
+
protocol_string: The string to parse.
|
107
|
+
|
108
|
+
Returns:
|
109
|
+
The corresponding protocol number.
|
110
|
+
|
111
|
+
Raises:
|
112
|
+
ValueError: If the protocol_string is not a valid protocol string.
|
113
|
+
"""
|
114
|
+
try:
|
115
|
+
protocol = socket.getprotobyname(protocol_string)
|
116
|
+
except (socket.error, TypeError):
|
117
|
+
try:
|
118
|
+
protocol = int(protocol_string)
|
119
|
+
except (ValueError, TypeError):
|
120
|
+
raise ValueError('Invalid protocol: %s' % protocol_string)
|
121
|
+
|
122
|
+
return protocol
|
123
|
+
|
124
|
+
|
125
|
+
def ReplacePortNames(port_range_string):
|
126
|
+
"""Replace port names with port numbers in a port-range string.
|
127
|
+
|
128
|
+
Args:
|
129
|
+
port_range_string: The string to parse.
|
130
|
+
|
131
|
+
Returns:
|
132
|
+
A port range string specifying ports only by number.
|
133
|
+
|
134
|
+
Raises:
|
135
|
+
ValueError: If the port_range_string is the wrong type or malformed.
|
136
|
+
"""
|
137
|
+
if not isinstance(port_range_string, basestring):
|
138
|
+
raise ValueError('Invalid port range: %s' % port_range_string)
|
139
|
+
|
140
|
+
ports = port_range_string.split('-')
|
141
|
+
if len(ports) not in [1, 2]:
|
142
|
+
raise ValueError('Invalid port range: %s' % port_range_string)
|
143
|
+
|
144
|
+
try:
|
145
|
+
low_port = socket.getservbyname(ports[0])
|
146
|
+
except socket.error:
|
147
|
+
low_port = int(ports[0])
|
148
|
+
|
149
|
+
try:
|
150
|
+
high_port = socket.getservbyname(ports[-1])
|
151
|
+
except socket.error:
|
152
|
+
high_port = int(ports[-1])
|
153
|
+
|
154
|
+
if low_port == high_port:
|
155
|
+
return '%d' % low_port
|
156
|
+
else:
|
157
|
+
return '%d-%d' % (low_port, high_port)
|
158
|
+
|
159
|
+
|
160
|
+
def Singularize(string):
|
161
|
+
"""A naive function for singularizing Compute Engine collection names."""
|
162
|
+
return string[:len(string) - 1] if string.endswith('s') else string
|
163
|
+
|
164
|
+
|
165
|
+
def All(func, project, max_results=None, filter=None, zone=None):
|
166
|
+
"""Calls the given list function while taking care of paging logic.
|
167
|
+
|
168
|
+
Args:
|
169
|
+
func: A Google Compute Engine list function.
|
170
|
+
project: The project to query.
|
171
|
+
max_results: The maximum number of items to return.
|
172
|
+
filter: The filter expression to plumb through.
|
173
|
+
zone: The zone for list functions that require a zone.
|
174
|
+
|
175
|
+
Returns:
|
176
|
+
A list of the resources.
|
177
|
+
"""
|
178
|
+
params = {
|
179
|
+
'project': project,
|
180
|
+
'maxResults': max_results,
|
181
|
+
'filter': filter}
|
182
|
+
|
183
|
+
if zone:
|
184
|
+
params['zone'] = zone
|
185
|
+
|
186
|
+
items = []
|
187
|
+
while True:
|
188
|
+
res = func(**params).execute()
|
189
|
+
kind = res.get('kind')
|
190
|
+
items.extend(res.get('items', []))
|
191
|
+
|
192
|
+
next_page_token = res.get('nextPageToken')
|
193
|
+
if not next_page_token:
|
194
|
+
break
|
195
|
+
|
196
|
+
params['pageToken'] = next_page_token
|
197
|
+
|
198
|
+
if max_results is not None:
|
199
|
+
items = items[:max_results]
|
200
|
+
return {'kind': kind,
|
201
|
+
'items': items}
|
202
|
+
|
203
|
+
|
204
|
+
def AllNames(func, project, max_results=None, filter=None, zone=None):
|
205
|
+
"""Like All, except returns a list of the names of the resources."""
|
206
|
+
list_res = All(
|
207
|
+
func, project, max_results=max_results, filter=filter, zone=zone)
|
208
|
+
return [resource.get('name') for resource in list_res.get('items', [])]
|