gcloud 0.0.2 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. data.tar.gz.sig +2 -3
  2. data/CHANGELOG +4 -0
  3. data/LICENSE +674 -0
  4. data/Manifest +111 -0
  5. data/README.md +4 -3
  6. data/bin/gcutil +53 -0
  7. data/gcloud.gemspec +4 -3
  8. data/packages/gcutil-1.7.1/CHANGELOG +197 -0
  9. data/packages/gcutil-1.7.1/LICENSE +202 -0
  10. data/packages/gcutil-1.7.1/VERSION +1 -0
  11. data/packages/gcutil-1.7.1/gcutil +53 -0
  12. data/packages/gcutil-1.7.1/lib/google_api_python_client/LICENSE +23 -0
  13. data/packages/gcutil-1.7.1/lib/google_api_python_client/apiclient/__init__.py +1 -0
  14. data/packages/gcutil-1.7.1/lib/google_api_python_client/apiclient/discovery.py +743 -0
  15. data/packages/gcutil-1.7.1/lib/google_api_python_client/apiclient/errors.py +123 -0
  16. data/packages/gcutil-1.7.1/lib/google_api_python_client/apiclient/ext/__init__.py +0 -0
  17. data/packages/gcutil-1.7.1/lib/google_api_python_client/apiclient/http.py +1443 -0
  18. data/packages/gcutil-1.7.1/lib/google_api_python_client/apiclient/mimeparse.py +172 -0
  19. data/packages/gcutil-1.7.1/lib/google_api_python_client/apiclient/model.py +385 -0
  20. data/packages/gcutil-1.7.1/lib/google_api_python_client/apiclient/schema.py +303 -0
  21. data/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/__init__.py +1 -0
  22. data/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/anyjson.py +32 -0
  23. data/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/appengine.py +528 -0
  24. data/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/client.py +1139 -0
  25. data/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/clientsecrets.py +105 -0
  26. data/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/crypt.py +244 -0
  27. data/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/django_orm.py +124 -0
  28. data/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/file.py +107 -0
  29. data/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/locked_file.py +343 -0
  30. data/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/multistore_file.py +379 -0
  31. data/packages/gcutil-1.7.1/lib/google_api_python_client/oauth2client/tools.py +174 -0
  32. data/packages/gcutil-1.7.1/lib/google_api_python_client/uritemplate/__init__.py +147 -0
  33. data/packages/gcutil-1.7.1/lib/google_apputils/LICENSE +202 -0
  34. data/packages/gcutil-1.7.1/lib/google_apputils/google/__init__.py +3 -0
  35. data/packages/gcutil-1.7.1/lib/google_apputils/google/apputils/__init__.py +3 -0
  36. data/packages/gcutil-1.7.1/lib/google_apputils/google/apputils/app.py +356 -0
  37. data/packages/gcutil-1.7.1/lib/google_apputils/google/apputils/appcommands.py +783 -0
  38. data/packages/gcutil-1.7.1/lib/google_apputils/google/apputils/basetest.py +1260 -0
  39. data/packages/gcutil-1.7.1/lib/google_apputils/google/apputils/datelib.py +421 -0
  40. data/packages/gcutil-1.7.1/lib/google_apputils/google/apputils/debug.py +60 -0
  41. data/packages/gcutil-1.7.1/lib/google_apputils/google/apputils/file_util.py +181 -0
  42. data/packages/gcutil-1.7.1/lib/google_apputils/google/apputils/resources.py +67 -0
  43. data/packages/gcutil-1.7.1/lib/google_apputils/google/apputils/run_script_module.py +217 -0
  44. data/packages/gcutil-1.7.1/lib/google_apputils/google/apputils/setup_command.py +159 -0
  45. data/packages/gcutil-1.7.1/lib/google_apputils/google/apputils/shellutil.py +49 -0
  46. data/packages/gcutil-1.7.1/lib/google_apputils/google/apputils/stopwatch.py +204 -0
  47. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/__init__.py +0 -0
  48. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/auth_helper.py +140 -0
  49. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/auth_helper_test.py +149 -0
  50. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/auto_auth.py +130 -0
  51. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/auto_auth_test.py +75 -0
  52. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/basic_cmds.py +128 -0
  53. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/basic_cmds_test.py +111 -0
  54. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/command_base.py +1808 -0
  55. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/command_base_test.py +1651 -0
  56. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/compute/v1beta13.json +2851 -0
  57. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/compute/v1beta14.json +3361 -0
  58. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/disk_cmds.py +342 -0
  59. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/disk_cmds_test.py +474 -0
  60. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/firewall_cmds.py +344 -0
  61. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/firewall_cmds_test.py +231 -0
  62. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/flags_cache.py +274 -0
  63. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/gcutil +89 -0
  64. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/gcutil_logging.py +69 -0
  65. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/image_cmds.py +262 -0
  66. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/image_cmds_test.py +172 -0
  67. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/instance_cmds.py +1506 -0
  68. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/instance_cmds_test.py +1904 -0
  69. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/kernel_cmds.py +91 -0
  70. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/kernel_cmds_test.py +56 -0
  71. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/machine_type_cmds.py +106 -0
  72. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/machine_type_cmds_test.py +59 -0
  73. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/metadata.py +96 -0
  74. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/metadata_lib.py +357 -0
  75. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/metadata_test.py +84 -0
  76. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/mock_api.py +420 -0
  77. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/mock_metadata.py +58 -0
  78. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/move_cmds.py +824 -0
  79. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/move_cmds_test.py +307 -0
  80. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/network_cmds.py +178 -0
  81. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/network_cmds_test.py +133 -0
  82. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/operation_cmds.py +181 -0
  83. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/operation_cmds_test.py +196 -0
  84. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/path_initializer.py +38 -0
  85. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/project_cmds.py +173 -0
  86. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/project_cmds_test.py +111 -0
  87. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/scopes.py +61 -0
  88. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/scopes_test.py +50 -0
  89. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/snapshot_cmds.py +276 -0
  90. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/snapshot_cmds_test.py +260 -0
  91. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/ssh_keys.py +266 -0
  92. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/ssh_keys_test.py +128 -0
  93. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/table_formatter.py +563 -0
  94. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/thread_pool.py +188 -0
  95. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/thread_pool_test.py +88 -0
  96. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/utils.py +208 -0
  97. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/utils_test.py +193 -0
  98. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/version.py +17 -0
  99. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/version_checker.py +246 -0
  100. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/version_checker_test.py +271 -0
  101. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/zone_cmds.py +151 -0
  102. data/packages/gcutil-1.7.1/lib/google_compute_engine/gcutil/zone_cmds_test.py +60 -0
  103. data/packages/gcutil-1.7.1/lib/httplib2/LICENSE +21 -0
  104. data/packages/gcutil-1.7.1/lib/httplib2/httplib2/__init__.py +1630 -0
  105. data/packages/gcutil-1.7.1/lib/httplib2/httplib2/cacerts.txt +714 -0
  106. data/packages/gcutil-1.7.1/lib/httplib2/httplib2/iri2uri.py +110 -0
  107. data/packages/gcutil-1.7.1/lib/httplib2/httplib2/socks.py +438 -0
  108. data/packages/gcutil-1.7.1/lib/iso8601/LICENSE +20 -0
  109. data/packages/gcutil-1.7.1/lib/iso8601/iso8601/__init__.py +1 -0
  110. data/packages/gcutil-1.7.1/lib/iso8601/iso8601/iso8601.py +102 -0
  111. data/packages/gcutil-1.7.1/lib/iso8601/iso8601/test_iso8601.py +111 -0
  112. data/packages/gcutil-1.7.1/lib/python_gflags/AUTHORS +2 -0
  113. data/packages/gcutil-1.7.1/lib/python_gflags/LICENSE +28 -0
  114. data/packages/gcutil-1.7.1/lib/python_gflags/gflags.py +2862 -0
  115. data/packages/gcutil-1.7.1/lib/python_gflags/gflags2man.py +544 -0
  116. data/packages/gcutil-1.7.1/lib/python_gflags/gflags_validators.py +187 -0
  117. metadata +118 -5
  118. 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', [])]