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.
- 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,38 @@
|
|
|
1
|
+
#!/usr/bin/python
|
|
2
|
+
#
|
|
3
|
+
# Copyright 2012 Google Inc. All Rights Reserved.
|
|
4
|
+
#
|
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
# you may not use this file except in compliance with the License.
|
|
7
|
+
# You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
# See the License for the specific language governing permissions and
|
|
15
|
+
# limitations under the License.
|
|
16
|
+
|
|
17
|
+
"""Contains a utility for initializing the system path."""
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
import os
|
|
22
|
+
import sys
|
|
23
|
+
|
|
24
|
+
def InitializeSysPath():
|
|
25
|
+
"""Adds gcutil's dependencies to sys.path.
|
|
26
|
+
|
|
27
|
+
This function assumes that the site module has not been
|
|
28
|
+
imported. The site module can be supressed by launching this program
|
|
29
|
+
using python -S.
|
|
30
|
+
"""
|
|
31
|
+
lib_dir = os.path.dirname(os.path.dirname(os.path.dirname(
|
|
32
|
+
os.path.realpath(__file__))))
|
|
33
|
+
libs = [os.path.join(lib_dir, lib) for lib in os.listdir(lib_dir)]
|
|
34
|
+
|
|
35
|
+
# Removes entries from libs that are already on the path.
|
|
36
|
+
libs = list(set(libs) - set(sys.path))
|
|
37
|
+
|
|
38
|
+
sys.path = libs + sys.path
|
|
@@ -0,0 +1,173 @@
|
|
|
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
|
+
"""Commands for interacting with Google Compute Engine."""
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
import httplib2
|
|
20
|
+
import json
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
from google.apputils import appcommands
|
|
24
|
+
import gflags as flags
|
|
25
|
+
|
|
26
|
+
from gcutil import auth_helper
|
|
27
|
+
from gcutil import command_base
|
|
28
|
+
from gcutil import gcutil_logging
|
|
29
|
+
from gcutil import metadata
|
|
30
|
+
from gcutil import scopes
|
|
31
|
+
from gcutil import version
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
FLAGS = flags.FLAGS
|
|
35
|
+
LOGGER = gcutil_logging.LOGGER
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class ProjectCommand(command_base.GoogleComputeCommand):
|
|
39
|
+
"""Base command for working with the projects collection."""
|
|
40
|
+
|
|
41
|
+
default_sort_field = 'name'
|
|
42
|
+
summary_fields = (('name', 'name'),
|
|
43
|
+
('ips', 'externalIpAddresses'))
|
|
44
|
+
|
|
45
|
+
def __init__(self, name, flag_values):
|
|
46
|
+
super(ProjectCommand, self).__init__(name, flag_values)
|
|
47
|
+
|
|
48
|
+
def SetApi(self, api):
|
|
49
|
+
"""Set the Google Compute Engine API for the command.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
api: The Google Compute Engine API used by this command.
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
None.
|
|
56
|
+
|
|
57
|
+
"""
|
|
58
|
+
self._projects_api = api.projects()
|
|
59
|
+
|
|
60
|
+
def CustomizePrintResult(self, result, table):
|
|
61
|
+
"""Customized result printing for this type.
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
result: json dictionary returned by the server
|
|
65
|
+
table: the pretty printing table to be customized
|
|
66
|
+
|
|
67
|
+
Returns:
|
|
68
|
+
None.
|
|
69
|
+
|
|
70
|
+
"""
|
|
71
|
+
# Add the quotas
|
|
72
|
+
table.AddRow(('', ''))
|
|
73
|
+
table.AddRow(('usage', ''))
|
|
74
|
+
for quota in result.get('quotas', []):
|
|
75
|
+
table.AddRow((' %s' % quota['metric'].lower().replace('_', '-'),
|
|
76
|
+
'%s/%s' % (str(quota['usage']), str(quota['limit']))))
|
|
77
|
+
# Add the metadata
|
|
78
|
+
if result.get('commonInstanceMetadata', []):
|
|
79
|
+
table.AddRow(('', ''))
|
|
80
|
+
table.AddRow(('common-instance-metadata', ''))
|
|
81
|
+
metadata_container = result.get('commonInstanceMetadata', [])
|
|
82
|
+
if 'kind' in metadata_container:
|
|
83
|
+
metadata_container = metadata_container.get('items', [])
|
|
84
|
+
for metadata_entry in metadata_container:
|
|
85
|
+
table.AddRow((' %s' % metadata_entry.get('key', ''),
|
|
86
|
+
self._PresentElement(metadata_entry.get('value', ''))))
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class GetProject(ProjectCommand):
|
|
90
|
+
"""Get the resource for a Google Compute Engine project."""
|
|
91
|
+
|
|
92
|
+
positional_args = '<project-name>'
|
|
93
|
+
|
|
94
|
+
# The remaining complex fields are filled in via CustomizePrintResult
|
|
95
|
+
detail_fields = (('name', 'name'),
|
|
96
|
+
('description', 'description'),
|
|
97
|
+
('creation-time', 'creationTimestamp'),
|
|
98
|
+
('ips', 'externalIpAddresses'))
|
|
99
|
+
|
|
100
|
+
def __init__(self, name, flag_values):
|
|
101
|
+
super(GetProject, self).__init__(name, flag_values)
|
|
102
|
+
|
|
103
|
+
def Handle(self, project=None):
|
|
104
|
+
"""Get the specified project.
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
project: The project for which to get defails.
|
|
108
|
+
|
|
109
|
+
Returns:
|
|
110
|
+
The result of getting the project.
|
|
111
|
+
"""
|
|
112
|
+
project = project or self._flags.project
|
|
113
|
+
project_request = self._projects_api.get(project=project)
|
|
114
|
+
return project_request.execute()
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
class SetCommonInstanceMetadata(ProjectCommand):
|
|
118
|
+
"""Set the commonInstanceMetadata field for a Google Compute Engine project.
|
|
119
|
+
|
|
120
|
+
This is a blanket overwrite of all of the project wide metadata.
|
|
121
|
+
"""
|
|
122
|
+
|
|
123
|
+
def __init__(self, name, flag_values):
|
|
124
|
+
super(SetCommonInstanceMetadata, self).__init__(name, flag_values)
|
|
125
|
+
flags.DEFINE_bool('force',
|
|
126
|
+
False,
|
|
127
|
+
'Set the metadata even if it erases existing keys',
|
|
128
|
+
flag_values=flag_values,
|
|
129
|
+
short_name='f')
|
|
130
|
+
self._metadata_flags_processor = metadata.MetadataFlagsProcessor(
|
|
131
|
+
flag_values)
|
|
132
|
+
|
|
133
|
+
def Handle(self):
|
|
134
|
+
"""Set the metadata common to all instances in the specified project.
|
|
135
|
+
|
|
136
|
+
Args:
|
|
137
|
+
None.
|
|
138
|
+
|
|
139
|
+
Returns:
|
|
140
|
+
The result of setting the project wide metadata.
|
|
141
|
+
|
|
142
|
+
Raises:
|
|
143
|
+
|
|
144
|
+
command_base.CommandError: If the update would cause some metadata to
|
|
145
|
+
be deleted.
|
|
146
|
+
"""
|
|
147
|
+
new_metadata = self._metadata_flags_processor.GatherMetadata()
|
|
148
|
+
|
|
149
|
+
if not self._flags.force:
|
|
150
|
+
get_request = self._projects_api.get(project=self._flags.project)
|
|
151
|
+
project_resource = get_request.execute()
|
|
152
|
+
project_metadata = project_resource.get('commonInstanceMetadata', [])
|
|
153
|
+
if 'kind' in project_metadata:
|
|
154
|
+
project_metadata = project_metadata.get('items', [])
|
|
155
|
+
existing_keys = set([entry['key'] for entry in project_metadata])
|
|
156
|
+
new_keys = set([entry['key'] for entry in new_metadata])
|
|
157
|
+
dropped_keys = existing_keys - new_keys
|
|
158
|
+
if dropped_keys:
|
|
159
|
+
raise command_base.CommandError(
|
|
160
|
+
'Discarding update that would wipe out the following metadata: %s.'
|
|
161
|
+
'\n\nRe-run with the -f flag to force the update.' %
|
|
162
|
+
', '.join(list(dropped_keys)))
|
|
163
|
+
|
|
164
|
+
project_request = self._projects_api.setCommonInstanceMetadata(
|
|
165
|
+
project=self._flags.project,
|
|
166
|
+
body={'kind': self._GetResourceApiKind('metadata'),
|
|
167
|
+
'items': new_metadata})
|
|
168
|
+
return project_request.execute()
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def AddCommands():
|
|
172
|
+
appcommands.AddCmd('getproject', GetProject)
|
|
173
|
+
appcommands.AddCmd('setcommoninstancemetadata', SetCommonInstanceMetadata)
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
#!/usr/bin/python
|
|
2
|
+
#
|
|
3
|
+
# Copyright 2012 Google Inc. All Rights Reserved.
|
|
4
|
+
#
|
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
# you may not use this file except in compliance with the License.
|
|
7
|
+
# You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
# See the License for the specific language governing permissions and
|
|
15
|
+
# limitations under the License.
|
|
16
|
+
|
|
17
|
+
"""Unit tests for the project commands."""
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
import path_initializer
|
|
22
|
+
path_initializer.InitializeSysPath()
|
|
23
|
+
|
|
24
|
+
import copy
|
|
25
|
+
import re
|
|
26
|
+
import tempfile
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
import gflags as flags
|
|
30
|
+
import unittest
|
|
31
|
+
|
|
32
|
+
from gcutil import auth_helper
|
|
33
|
+
from gcutil import command_base
|
|
34
|
+
from gcutil import flags_cache
|
|
35
|
+
from gcutil import mock_api
|
|
36
|
+
from gcutil import project_cmds
|
|
37
|
+
|
|
38
|
+
FLAGS = flags.FLAGS
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class ComputeCmdsTest(unittest.TestCase):
|
|
42
|
+
def testGetProjectGeneratesCorrectRequest(self):
|
|
43
|
+
flag_values = copy.deepcopy(FLAGS)
|
|
44
|
+
|
|
45
|
+
command = project_cmds.GetProject('getproject', flag_values)
|
|
46
|
+
|
|
47
|
+
expected_project = 'test_project'
|
|
48
|
+
flag_values.project = expected_project
|
|
49
|
+
|
|
50
|
+
command.SetFlags(flag_values)
|
|
51
|
+
command.SetApi(mock_api.MockApi())
|
|
52
|
+
|
|
53
|
+
result = command.Handle()
|
|
54
|
+
|
|
55
|
+
self.assertEqual(result['project'], expected_project)
|
|
56
|
+
|
|
57
|
+
def testSetCommonInstanceMetadataGeneratesCorrectRequest(self):
|
|
58
|
+
|
|
59
|
+
class SetCommonInstanceMetadata(object):
|
|
60
|
+
|
|
61
|
+
def __call__(self, project, body):
|
|
62
|
+
self._project = project
|
|
63
|
+
self._body = body
|
|
64
|
+
return self
|
|
65
|
+
|
|
66
|
+
def execute(self):
|
|
67
|
+
return {'project': self._project, 'body': self._body}
|
|
68
|
+
|
|
69
|
+
flag_values = copy.deepcopy(FLAGS)
|
|
70
|
+
command = project_cmds.SetCommonInstanceMetadata(
|
|
71
|
+
'setcommoninstancemetadata', flag_values)
|
|
72
|
+
|
|
73
|
+
expected_project = 'test_project'
|
|
74
|
+
flag_values.project = expected_project
|
|
75
|
+
flag_values.service_version = 'v1beta13'
|
|
76
|
+
with tempfile.NamedTemporaryFile() as metadata_file:
|
|
77
|
+
metadata_file.write('foo:bar')
|
|
78
|
+
metadata_file.flush()
|
|
79
|
+
flag_values.metadata_from_file = ['sshKeys:%s' % metadata_file.name]
|
|
80
|
+
|
|
81
|
+
command.SetFlags(flag_values)
|
|
82
|
+
command.SetApi(mock_api.MockApi())
|
|
83
|
+
command._projects_api.get = mock_api.CommandExecutor(
|
|
84
|
+
{'commonInstanceMetadata': [{'key': 'sshKeys', 'value': ''}]})
|
|
85
|
+
command._projects_api.setCommonInstanceMetadata = (
|
|
86
|
+
SetCommonInstanceMetadata())
|
|
87
|
+
|
|
88
|
+
result = command.Handle()
|
|
89
|
+
self.assertEquals(expected_project, result['project'])
|
|
90
|
+
self.assertEquals(
|
|
91
|
+
{'kind': 'compute#metadata',
|
|
92
|
+
'items': [{'key': 'sshKeys', 'value': 'foo:bar'}]},
|
|
93
|
+
result['body'])
|
|
94
|
+
|
|
95
|
+
def testSetCommonInstanceMetadataChecksForOverwrites(self):
|
|
96
|
+
flag_values = copy.deepcopy(FLAGS)
|
|
97
|
+
command = project_cmds.SetCommonInstanceMetadata(
|
|
98
|
+
'setcommoninstancemetadata', flag_values)
|
|
99
|
+
|
|
100
|
+
expected_project = 'test_project'
|
|
101
|
+
flag_values.project = expected_project
|
|
102
|
+
flag_values.service_version = 'v1beta13'
|
|
103
|
+
command.SetFlags(flag_values)
|
|
104
|
+
command.SetApi(mock_api.MockApi())
|
|
105
|
+
command._projects_api.get = mock_api.CommandExecutor(
|
|
106
|
+
{'commonInstanceMetadata': [{'key': 'sshKeys', 'value': 'foo:bar'}]})
|
|
107
|
+
|
|
108
|
+
self.assertRaises(command_base.CommandError, command.Handle)
|
|
109
|
+
|
|
110
|
+
if __name__ == '__main__':
|
|
111
|
+
unittest.main()
|
|
@@ -0,0 +1,61 @@
|
|
|
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
|
+
"""The Google API scopes used by Google Compute Engine."""
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
USER_INFO_SCOPE = 'https://www.googleapis.com/auth/userinfo.email'
|
|
20
|
+
COMPUTE_RW_SCOPE = 'https://www.googleapis.com/auth/compute'
|
|
21
|
+
COMPUTE_RO_SCOPE = 'https://www.googleapis.com/auth/compute.readonly'
|
|
22
|
+
STORAGE_R_SCOPE = 'https://www.googleapis.com/auth/devstorage.read_only'
|
|
23
|
+
STORAGE_W_SCOPE = 'https://www.googleapis.com/auth/devstorage.write_only'
|
|
24
|
+
STORAGE_RW_SCOPE = (
|
|
25
|
+
'https://www.googleapis.com/auth/devstorage.read_write')
|
|
26
|
+
STORAGE_FULL_SCOPE = 'https://www.googleapis.com/auth/devstorage.full_control'
|
|
27
|
+
DEFAULT_AUTH_SCOPES = [
|
|
28
|
+
COMPUTE_RW_SCOPE, COMPUTE_RO_SCOPE,
|
|
29
|
+
# USER_INFO_SCOPE allows seeing who user authenticated as.
|
|
30
|
+
USER_INFO_SCOPE,
|
|
31
|
+
# Image commands use STORAGE access.
|
|
32
|
+
STORAGE_R_SCOPE, STORAGE_W_SCOPE, STORAGE_RW_SCOPE, STORAGE_FULL_SCOPE
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
TASKQUEUE = 'https://www.googleapis.com/auth/taskqueue'
|
|
36
|
+
BIGQUERY = 'https://www.googleapis.com/auth/bigquery'
|
|
37
|
+
CLOUDSQL = 'https://www.googleapis.com/auth/sqlservice'
|
|
38
|
+
|
|
39
|
+
SCOPE_ALIASES = {
|
|
40
|
+
'bigquery': BIGQUERY,
|
|
41
|
+
'cloudsql': CLOUDSQL,
|
|
42
|
+
'compute-ro': COMPUTE_RO_SCOPE,
|
|
43
|
+
'compute-rw': COMPUTE_RW_SCOPE,
|
|
44
|
+
'storage-full': STORAGE_FULL_SCOPE,
|
|
45
|
+
'storage-ro': STORAGE_R_SCOPE,
|
|
46
|
+
'storage-rw': STORAGE_RW_SCOPE,
|
|
47
|
+
'storage-wo': STORAGE_W_SCOPE,
|
|
48
|
+
'taskqueue': TASKQUEUE,
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def ExpandScopeAliases(scopes):
|
|
53
|
+
"""Expand a list of scope aliases if available.
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
scopes: a list of scopes and scope aliases
|
|
57
|
+
|
|
58
|
+
Returns:
|
|
59
|
+
a list of scopes
|
|
60
|
+
"""
|
|
61
|
+
return [SCOPE_ALIASES.get(s, s) for s in scopes]
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
#!/usr/bin/python
|
|
2
|
+
#
|
|
3
|
+
# Copyright 2012 Google Inc. All Rights Reserved.
|
|
4
|
+
#
|
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
# you may not use this file except in compliance with the License.
|
|
7
|
+
# You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
# See the License for the specific language governing permissions and
|
|
15
|
+
# limitations under the License.
|
|
16
|
+
|
|
17
|
+
"""Tests for scopes module."""
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
import path_initializer
|
|
22
|
+
path_initializer.InitializeSysPath()
|
|
23
|
+
|
|
24
|
+
import unittest
|
|
25
|
+
from gcutil import scopes
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class ScopesTest(unittest.TestCase):
|
|
29
|
+
def testExpandPassthrough(self):
|
|
30
|
+
scopes_in = ['foo', 'bar', scopes.TASKQUEUE]
|
|
31
|
+
scopes_out = scopes.ExpandScopeAliases(scopes_in)
|
|
32
|
+
self.assertEqual(scopes_out, scopes_in)
|
|
33
|
+
|
|
34
|
+
def testExpandEmpty(self):
|
|
35
|
+
scopes_in = []
|
|
36
|
+
scopes_out = scopes.ExpandScopeAliases(scopes_in)
|
|
37
|
+
self.assertEqual(scopes_out, scopes_in)
|
|
38
|
+
|
|
39
|
+
def testExpandSingle(self):
|
|
40
|
+
scopes_in = ['compute-rw']
|
|
41
|
+
scopes_out = scopes.ExpandScopeAliases(scopes_in)
|
|
42
|
+
self.assertEqual(scopes_out, [scopes.COMPUTE_RW_SCOPE])
|
|
43
|
+
|
|
44
|
+
def testExpandMixed(self):
|
|
45
|
+
scopes_in = ['compute-rw', scopes.TASKQUEUE]
|
|
46
|
+
scopes_out = scopes.ExpandScopeAliases(scopes_in)
|
|
47
|
+
self.assertEqual(scopes_out, [scopes.COMPUTE_RW_SCOPE, scopes.TASKQUEUE])
|
|
48
|
+
|
|
49
|
+
if __name__ == '__main__':
|
|
50
|
+
unittest.main()
|
|
@@ -0,0 +1,276 @@
|
|
|
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
|
+
"""Commands for interacting with Google Compute Engine disk snapshots."""
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
import time
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
from google.apputils import appcommands
|
|
23
|
+
import gflags as flags
|
|
24
|
+
|
|
25
|
+
from gcutil import command_base
|
|
26
|
+
from gcutil import gcutil_logging
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
FLAGS = flags.FLAGS
|
|
30
|
+
LOGGER = gcutil_logging.LOGGER
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class SnapshotCommand(command_base.GoogleComputeCommand):
|
|
34
|
+
"""Base command for working with the snapshots collection."""
|
|
35
|
+
|
|
36
|
+
default_sort_field = 'name'
|
|
37
|
+
summary_fields = (('name', 'name'),
|
|
38
|
+
('description', 'description'),
|
|
39
|
+
('creation-time', 'creationTimestamp'),
|
|
40
|
+
('status', 'status'),
|
|
41
|
+
('disk-size-gb', 'diskSizeGb'),
|
|
42
|
+
('source-disk', 'sourceDisk'))
|
|
43
|
+
|
|
44
|
+
detail_fields = (('name', 'name'),
|
|
45
|
+
('description', 'description'),
|
|
46
|
+
('creation-time', 'creationTimestamp'),
|
|
47
|
+
('status', 'status'),
|
|
48
|
+
('disk-size-gb', 'diskSizeGb'),
|
|
49
|
+
('source-disk', 'sourceDisk'))
|
|
50
|
+
|
|
51
|
+
resource_collection_name = 'snapshots'
|
|
52
|
+
|
|
53
|
+
def __init__(self, name, flag_values):
|
|
54
|
+
super(SnapshotCommand, self).__init__(name, flag_values)
|
|
55
|
+
|
|
56
|
+
def SetApi(self, api):
|
|
57
|
+
"""Set the Google Compute Engine API for the command.
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
api: The Google Compute Engine API used by this command.
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
None.
|
|
64
|
+
|
|
65
|
+
"""
|
|
66
|
+
self._snapshots_api = api.snapshots()
|
|
67
|
+
self._disks_api = api.disks()
|
|
68
|
+
self._zones_api = api.zones()
|
|
69
|
+
|
|
70
|
+
def _PrepareRequestArgs(self, snapshot_name, **other_args):
|
|
71
|
+
"""Gets the dictionary of API method keyword arguments.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
snapshot_name: The name of the snapshot.
|
|
75
|
+
**other_args: Keyword arguments that should be included in the request.
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
Dictionary of keyword arguments that should be passed in the API call,
|
|
79
|
+
includes all keyword arguments passed in 'other_args' plus
|
|
80
|
+
common keys such as the name of the resource and the project.
|
|
81
|
+
"""
|
|
82
|
+
|
|
83
|
+
kwargs = {
|
|
84
|
+
'project': self._project,
|
|
85
|
+
'snapshot': self.DenormalizeResourceName(snapshot_name)
|
|
86
|
+
}
|
|
87
|
+
for key, value in other_args.items():
|
|
88
|
+
kwargs[key] = value
|
|
89
|
+
return kwargs
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class AddSnapshot(SnapshotCommand):
|
|
93
|
+
"""Create a new persistent disk snapshot."""
|
|
94
|
+
|
|
95
|
+
positional_args = '<snapshot-name>'
|
|
96
|
+
status_field = 'status'
|
|
97
|
+
_TERMINAL_STATUS = ['READY', 'FAILED']
|
|
98
|
+
|
|
99
|
+
def __init__(self, name, flag_values):
|
|
100
|
+
super(AddSnapshot, self).__init__(name, flag_values)
|
|
101
|
+
flags.DEFINE_string('description',
|
|
102
|
+
'',
|
|
103
|
+
'Snapshot description.',
|
|
104
|
+
flag_values=flag_values)
|
|
105
|
+
flags.DEFINE_string('source_disk',
|
|
106
|
+
None,
|
|
107
|
+
'The source disk for this snapshot.',
|
|
108
|
+
flag_values=flag_values)
|
|
109
|
+
flags.DEFINE_string('zone',
|
|
110
|
+
None,
|
|
111
|
+
'The zone of the disk.',
|
|
112
|
+
flag_values=flag_values)
|
|
113
|
+
flags.DEFINE_boolean('wait_until_complete',
|
|
114
|
+
False,
|
|
115
|
+
'Whether the program should wait until the snapshot'
|
|
116
|
+
' is complete.',
|
|
117
|
+
flag_values=flag_values)
|
|
118
|
+
|
|
119
|
+
def Handle(self, snapshot_name):
|
|
120
|
+
"""Add the specified snapshot.
|
|
121
|
+
|
|
122
|
+
Args:
|
|
123
|
+
snapshot_name: The name of the snapshot to add
|
|
124
|
+
|
|
125
|
+
Returns:
|
|
126
|
+
The result of inserting the snapshot.
|
|
127
|
+
"""
|
|
128
|
+
if not self._flags.source_disk:
|
|
129
|
+
disk = self._PromptForDisk()
|
|
130
|
+
if not disk:
|
|
131
|
+
raise command_base.CommandError(
|
|
132
|
+
'You cannot create a snapshot if you have no disks.')
|
|
133
|
+
self._flags.source_disk = disk['name']
|
|
134
|
+
|
|
135
|
+
zone = 'unused'
|
|
136
|
+
if self._IsUsingAtLeastApiVersion('v1beta14'):
|
|
137
|
+
zone = self.GetZoneForResource(self._disks_api, self._flags.source_disk)
|
|
138
|
+
|
|
139
|
+
source_disk = self.NormalizePerZoneResourceName(
|
|
140
|
+
self._project,
|
|
141
|
+
zone,
|
|
142
|
+
'disks',
|
|
143
|
+
self._flags.source_disk)
|
|
144
|
+
|
|
145
|
+
kwargs = {
|
|
146
|
+
'project': self._project,
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
snapshot_resource = {
|
|
150
|
+
'kind': self._GetResourceApiKind('snapshot'),
|
|
151
|
+
'name': self.DenormalizeResourceName(snapshot_name),
|
|
152
|
+
'description': self._flags.description,
|
|
153
|
+
'sourceDisk': source_disk
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
kwargs['body'] = snapshot_resource
|
|
157
|
+
snapshot_request = self._snapshots_api.insert(**kwargs)
|
|
158
|
+
|
|
159
|
+
result = snapshot_request.execute()
|
|
160
|
+
|
|
161
|
+
if self._flags.wait_until_complete:
|
|
162
|
+
result = self.WaitForOperation(self._flags, time, result)
|
|
163
|
+
if not result.get('error'):
|
|
164
|
+
result = self._InternalGetSnapshot(snapshot_name)
|
|
165
|
+
result = self._WaitUntilSnapshotIsComplete(result, snapshot_name)
|
|
166
|
+
|
|
167
|
+
return result
|
|
168
|
+
|
|
169
|
+
def _InternalGetSnapshot(self, snapshot_name):
|
|
170
|
+
"""A simple implementation of getting current snapshot state.
|
|
171
|
+
|
|
172
|
+
Args:
|
|
173
|
+
snapshot_name: the name of the snapshot to get.
|
|
174
|
+
|
|
175
|
+
Returns:
|
|
176
|
+
Json containing full snapshot information.
|
|
177
|
+
"""
|
|
178
|
+
snapshot_request = self._snapshots_api.get(
|
|
179
|
+
**self._PrepareRequestArgs(snapshot_name))
|
|
180
|
+
return snapshot_request.execute()
|
|
181
|
+
|
|
182
|
+
def _WaitUntilSnapshotIsComplete(self, result, snapshot_name):
|
|
183
|
+
"""Waits for the snapshot to complete.
|
|
184
|
+
|
|
185
|
+
Periodically polls the server for current snapshot status. Exits if the
|
|
186
|
+
status of the snapshot is READY or FAILED or the maximum waiting
|
|
187
|
+
timeout has been reached. In both cases returns the last known snapshot
|
|
188
|
+
details.
|
|
189
|
+
|
|
190
|
+
Args:
|
|
191
|
+
result: the current state of the snapshot.
|
|
192
|
+
snapshot_name: the name of the snapshot to watch.
|
|
193
|
+
|
|
194
|
+
Returns:
|
|
195
|
+
Json containing full snapshot information.
|
|
196
|
+
"""
|
|
197
|
+
current_status = result[self.status_field]
|
|
198
|
+
start_time = time.time()
|
|
199
|
+
LOGGER.info('Will wait for snapshot to complete for: %d seconds.',
|
|
200
|
+
self._flags.max_wait_time)
|
|
201
|
+
while (time.time() - start_time < self._flags.max_wait_time and
|
|
202
|
+
current_status not in self._TERMINAL_STATUS):
|
|
203
|
+
LOGGER.info(
|
|
204
|
+
'Waiting for snapshot. Current status: %s. Sleeping for %ss.',
|
|
205
|
+
current_status, self._flags.sleep_between_polls)
|
|
206
|
+
time.sleep(self._flags.sleep_between_polls)
|
|
207
|
+
result = self._InternalGetSnapshot(snapshot_name)
|
|
208
|
+
current_status = result[self.status_field]
|
|
209
|
+
if current_status not in self._TERMINAL_STATUS:
|
|
210
|
+
LOGGER.warn('Timeout reached. Snapshot %s has not yet completed.',
|
|
211
|
+
snapshot_name)
|
|
212
|
+
return result
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
class GetSnapshot(SnapshotCommand):
|
|
216
|
+
"""Get a machine snapshot."""
|
|
217
|
+
|
|
218
|
+
positional_args = '<snapshot-name>'
|
|
219
|
+
|
|
220
|
+
def __init__(self, name, flag_values):
|
|
221
|
+
super(GetSnapshot, self).__init__(name, flag_values)
|
|
222
|
+
|
|
223
|
+
def Handle(self, snapshot_name):
|
|
224
|
+
"""Get the specified snapshot.
|
|
225
|
+
|
|
226
|
+
Args:
|
|
227
|
+
snapshot_name: The name of the snapshot to get
|
|
228
|
+
|
|
229
|
+
Returns:
|
|
230
|
+
The result of getting the snapshot.
|
|
231
|
+
"""
|
|
232
|
+
snapshot_request = self._snapshots_api.get(
|
|
233
|
+
**self._PrepareRequestArgs(snapshot_name))
|
|
234
|
+
|
|
235
|
+
return snapshot_request.execute()
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
class DeleteSnapshot(SnapshotCommand):
|
|
239
|
+
"""Delete one or more machine snapshots."""
|
|
240
|
+
|
|
241
|
+
positional_args = '<snapshot-name>'
|
|
242
|
+
safety_prompt = 'Delete snapshot'
|
|
243
|
+
|
|
244
|
+
def __init__(self, name, flag_values):
|
|
245
|
+
super(DeleteSnapshot, self).__init__(name, flag_values)
|
|
246
|
+
|
|
247
|
+
def Handle(self, *snapshot_names):
|
|
248
|
+
"""Delete the specified snapshots.
|
|
249
|
+
|
|
250
|
+
Args:
|
|
251
|
+
*snapshot_names: The names of the snapshots to delete
|
|
252
|
+
|
|
253
|
+
Returns:
|
|
254
|
+
Tuple (results, exceptions) - results of deleting the snapshots.
|
|
255
|
+
"""
|
|
256
|
+
requests = []
|
|
257
|
+
for snapshot_name in snapshot_names:
|
|
258
|
+
requests.append(self._snapshots_api.delete(
|
|
259
|
+
**self._PrepareRequestArgs(snapshot_name)))
|
|
260
|
+
results, exceptions = self.ExecuteRequests(requests)
|
|
261
|
+
return self.MakeListResult(results, 'operationList'), exceptions
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
class ListSnapshots(SnapshotCommand, command_base.GoogleComputeListCommand):
|
|
265
|
+
"""List the machine snapshots for a project."""
|
|
266
|
+
|
|
267
|
+
def ListFunc(self):
|
|
268
|
+
"""Returns the function for listing snapshots."""
|
|
269
|
+
return self._snapshots_api.list
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
def AddCommands():
|
|
273
|
+
appcommands.AddCmd('addsnapshot', AddSnapshot)
|
|
274
|
+
appcommands.AddCmd('getsnapshot', GetSnapshot)
|
|
275
|
+
appcommands.AddCmd('deletesnapshot', DeleteSnapshot)
|
|
276
|
+
appcommands.AddCmd('listsnapshots', ListSnapshots)
|