c2client 0.22__tar.gz → 0.24__tar.gz
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.
- {c2client-0.22/c2client.egg-info → c2client-0.24}/PKG-INFO +13 -3
- {c2client-0.22 → c2client-0.24}/README.rst +1 -1
- c2client-0.24/c2client/__init__.py +1 -0
- {c2client-0.22 → c2client-0.24}/c2client/clients.py +51 -97
- {c2client-0.22 → c2client-0.24}/c2client/errors.py +10 -0
- c2client-0.24/c2client/utils.py +134 -0
- {c2client-0.22 → c2client-0.24/c2client.egg-info}/PKG-INFO +13 -3
- {c2client-0.22 → c2client-0.24}/c2client.egg-info/entry_points.txt +1 -0
- {c2client-0.22 → c2client-0.24}/c2client.egg-info/requires.txt +0 -2
- {c2client-0.22 → c2client-0.24}/setup.py +8 -3
- c2client-0.22/c2client/__init__.py +0 -1
- c2client-0.22/c2client/utils.py +0 -64
- {c2client-0.22 → c2client-0.24}/MANIFEST.in +0 -0
- {c2client-0.22 → c2client-0.24}/Makefile +0 -0
- {c2client-0.22 → c2client-0.24}/c2client/c2rc_convert.py +0 -0
- {c2client-0.22 → c2client-0.24}/c2client.egg-info/SOURCES.txt +0 -0
- {c2client-0.22 → c2client-0.24}/c2client.egg-info/dependency_links.txt +0 -0
- {c2client-0.22 → c2client-0.24}/c2client.egg-info/top_level.txt +0 -0
- {c2client-0.22 → c2client-0.24}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: c2client
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.24
|
|
4
4
|
Summary: CROC Cloud Platform - API Client
|
|
5
5
|
Home-page: https://github.com/c2devel/c2-client
|
|
6
6
|
Author: CROC Cloud Team
|
|
@@ -8,12 +8,20 @@ Author-email: devel@croc.ru
|
|
|
8
8
|
Maintainer: Andrey Kulaev
|
|
9
9
|
Maintainer-email: adkulaev@gmail.com
|
|
10
10
|
License: GPL3
|
|
11
|
-
|
|
11
|
+
Platform: UNKNOWN
|
|
12
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
12
13
|
Classifier: Environment :: Console
|
|
13
14
|
Classifier: Intended Audience :: Developers
|
|
14
15
|
Classifier: Intended Audience :: System Administrators
|
|
15
16
|
Classifier: Operating System :: OS Independent
|
|
16
17
|
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.6
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.7
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
25
|
|
|
18
26
|
CROC Cloud API Client
|
|
19
27
|
=====================
|
|
@@ -21,7 +29,7 @@ CROC Cloud API Client
|
|
|
21
29
|
Simple command-line utility for sending custom requests to CROC Cloud platform.
|
|
22
30
|
|
|
23
31
|
**Warning: this utility is not intended for automation cases.
|
|
24
|
-
Use https://github.com/c2devel/
|
|
32
|
+
Use https://github.com/c2devel/boto3.git and python scripts instead.**
|
|
25
33
|
|
|
26
34
|
Installation
|
|
27
35
|
------------
|
|
@@ -66,3 +74,5 @@ Send simple request:
|
|
|
66
74
|
|
|
67
75
|
$ c2-ec2 RunInstances ImageId cmi-078880A0 Description "Test instance" \
|
|
68
76
|
InstanceType m1.small MaxCount 1 MinCount 1 SecurityGroup.1 test
|
|
77
|
+
|
|
78
|
+
|
|
@@ -4,7 +4,7 @@ CROC Cloud API Client
|
|
|
4
4
|
Simple command-line utility for sending custom requests to CROC Cloud platform.
|
|
5
5
|
|
|
6
6
|
**Warning: this utility is not intended for automation cases.
|
|
7
|
-
Use https://github.com/c2devel/
|
|
7
|
+
Use https://github.com/c2devel/boto3.git and python scripts instead.**
|
|
8
8
|
|
|
9
9
|
Installation
|
|
10
10
|
------------
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.24"
|
|
@@ -1,29 +1,20 @@
|
|
|
1
1
|
import argparse
|
|
2
2
|
import json
|
|
3
|
-
import os
|
|
4
3
|
import re
|
|
5
4
|
import ssl
|
|
6
5
|
from abc import abstractmethod
|
|
7
6
|
from functools import wraps
|
|
8
|
-
from typing import Dict
|
|
9
|
-
from urllib.parse import urlparse
|
|
7
|
+
from typing import Any, Dict, Optional
|
|
10
8
|
|
|
11
|
-
import boto
|
|
12
|
-
import boto.cloudtrail.layer1
|
|
13
|
-
import boto.ec2
|
|
14
|
-
import boto.ec2.cloudwatch
|
|
15
9
|
import boto3
|
|
16
10
|
import inflection
|
|
17
|
-
from boto.ec2.regioninfo import RegionInfo
|
|
18
11
|
|
|
19
|
-
from c2client.
|
|
12
|
+
from c2client.errors import InvalidMethodName
|
|
13
|
+
from c2client.utils import from_dot_notation, get_env_var, convert_args
|
|
20
14
|
|
|
21
15
|
|
|
22
16
|
ssl._create_default_https_context = ssl._create_unverified_context
|
|
23
17
|
|
|
24
|
-
if os.environ.get("DEBUG"):
|
|
25
|
-
boto.set_stream_logger("c2")
|
|
26
|
-
|
|
27
18
|
|
|
28
19
|
def exitcode(func: callable):
|
|
29
20
|
"""Wrapper for logging any caught exception."""
|
|
@@ -32,9 +23,6 @@ def exitcode(func: callable):
|
|
|
32
23
|
def wrapper(*args, **kwargs):
|
|
33
24
|
try:
|
|
34
25
|
func(*args, **kwargs)
|
|
35
|
-
except boto.exception.BotoServerError as error:
|
|
36
|
-
error_code = error.error_code or error.body.get("__type")
|
|
37
|
-
return f"{error_code}: {error.message}"
|
|
38
26
|
except Exception as e:
|
|
39
27
|
return e
|
|
40
28
|
return wrapper
|
|
@@ -94,38 +82,6 @@ class BaseClient:
|
|
|
94
82
|
print(response)
|
|
95
83
|
|
|
96
84
|
|
|
97
|
-
class C2ClientLegacy(BaseClient):
|
|
98
|
-
|
|
99
|
-
connection_class: type
|
|
100
|
-
|
|
101
|
-
@classmethod
|
|
102
|
-
def get_client(cls, verify: bool):
|
|
103
|
-
"""Return boto connection."""
|
|
104
|
-
|
|
105
|
-
if not boto.config.has_section("Boto"):
|
|
106
|
-
boto.config.add_section("Boto")
|
|
107
|
-
boto.config.set("Boto", "is_secure", "True")
|
|
108
|
-
boto.config.set("Boto", "num_retries", "0")
|
|
109
|
-
boto.config.set("Boto", "https_validate_certificates", str(verify))
|
|
110
|
-
|
|
111
|
-
parsed_endpoint = urlparse(get_env_var(cls.url_key))
|
|
112
|
-
|
|
113
|
-
return cls.connection_class(
|
|
114
|
-
port=parsed_endpoint.port,
|
|
115
|
-
path=parsed_endpoint.path,
|
|
116
|
-
region=RegionInfo(name=parsed_endpoint.hostname, endpoint=parsed_endpoint.hostname),
|
|
117
|
-
is_secure=False,
|
|
118
|
-
)
|
|
119
|
-
|
|
120
|
-
@classmethod
|
|
121
|
-
def make_request(cls, method: str, arguments: dict, verify: bool):
|
|
122
|
-
|
|
123
|
-
connection = cls.get_client(verify)
|
|
124
|
-
response = connection.make_request(method, arguments)
|
|
125
|
-
|
|
126
|
-
return prettify_xml(response.read())
|
|
127
|
-
|
|
128
|
-
|
|
129
85
|
class C2Client(BaseClient):
|
|
130
86
|
|
|
131
87
|
@classmethod
|
|
@@ -148,72 +104,82 @@ class C2Client(BaseClient):
|
|
|
148
104
|
)
|
|
149
105
|
|
|
150
106
|
@classmethod
|
|
151
|
-
def
|
|
152
|
-
"""Check whether type conversion is needed for argument."""
|
|
153
|
-
|
|
154
|
-
return True
|
|
155
|
-
|
|
156
|
-
@classmethod
|
|
157
|
-
def make_request(cls, method: str, arguments: dict, verify: bool):
|
|
107
|
+
def make_request(cls, method: str, arguments: Optional[Dict], verify: bool) -> str:
|
|
158
108
|
|
|
159
109
|
client = cls.get_client(verify)
|
|
160
110
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
elif value.lower() == "false":
|
|
169
|
-
arguments[key] = False
|
|
111
|
+
if not hasattr(client, inflection.underscore(method)):
|
|
112
|
+
raise InvalidMethodName(method)
|
|
113
|
+
|
|
114
|
+
if arguments:
|
|
115
|
+
arguments = cls.convert_fields_names(arguments)
|
|
116
|
+
shape = client.meta.service_model.operation_model(method).input_shape
|
|
117
|
+
arguments = convert_args(from_dot_notation(arguments), shape)
|
|
170
118
|
|
|
171
|
-
result = getattr(client, inflection.underscore(method))(**
|
|
119
|
+
result = getattr(client, inflection.underscore(method))(**arguments)
|
|
172
120
|
|
|
173
121
|
result.pop("ResponseMetadata", None)
|
|
174
122
|
|
|
175
123
|
# default=str is required for serializing Datetime objects
|
|
176
124
|
return json.dumps(result, indent=4, default=str)
|
|
177
125
|
|
|
126
|
+
@staticmethod
|
|
127
|
+
def convert_fields_names(arguments: dict) -> Dict[str, Any]:
|
|
128
|
+
"""Convert field names as in the documentation."""
|
|
129
|
+
|
|
130
|
+
return arguments
|
|
131
|
+
|
|
178
132
|
|
|
179
|
-
class EC2Client(
|
|
133
|
+
class EC2Client(C2Client):
|
|
180
134
|
|
|
181
135
|
url_key = "EC2_URL"
|
|
182
136
|
client_name = "ec2"
|
|
183
137
|
|
|
184
|
-
|
|
138
|
+
@staticmethod
|
|
139
|
+
def convert_fields_names(arguments: dict) -> Dict[str, Any]:
|
|
140
|
+
"""Convert field names as in the documentation."""
|
|
185
141
|
|
|
142
|
+
tag_pattern = r"Filter\.\d+\.Value"
|
|
186
143
|
|
|
187
|
-
|
|
144
|
+
new_arguments = {}
|
|
145
|
+
filters = {}
|
|
146
|
+
for key, value in arguments.items():
|
|
147
|
+
new_key = key
|
|
148
|
+
if re.fullmatch(tag_pattern, key):
|
|
149
|
+
key = ".".join(key.split(".")[:3])
|
|
188
150
|
|
|
189
|
-
|
|
190
|
-
|
|
151
|
+
if key in filters:
|
|
152
|
+
filters[key] += 1
|
|
153
|
+
else:
|
|
154
|
+
filters[key] = 1
|
|
191
155
|
|
|
192
|
-
|
|
156
|
+
new_key = f"{key}.{filters[key]}"
|
|
193
157
|
|
|
158
|
+
new_arguments[new_key] = value
|
|
194
159
|
|
|
195
|
-
|
|
160
|
+
return new_arguments
|
|
196
161
|
|
|
197
|
-
url_key = "AWS_CLOUDTRAIL_URL"
|
|
198
|
-
client_name = "ct"
|
|
199
162
|
|
|
200
|
-
|
|
163
|
+
class CWClient(C2Client):
|
|
201
164
|
|
|
202
|
-
|
|
203
|
-
|
|
165
|
+
url_key = "AWS_CLOUDWATCH_URL"
|
|
166
|
+
client_name = "cloudwatch"
|
|
167
|
+
|
|
168
|
+
@staticmethod
|
|
169
|
+
def convert_fields_names(arguments: dict) -> Dict[str, Any]:
|
|
170
|
+
"""Convert field names as in the documentation."""
|
|
204
171
|
|
|
205
|
-
|
|
172
|
+
new_arguments = {}
|
|
173
|
+
for key, value in arguments.items():
|
|
174
|
+
new_key = key.replace(".member.", ".")
|
|
175
|
+
new_arguments[new_key] = value
|
|
176
|
+
return new_arguments
|
|
206
177
|
|
|
207
|
-
if "MaxResults" in arguments:
|
|
208
|
-
arguments["MaxResults"] = int(arguments["MaxResults"])
|
|
209
|
-
if "StartTime" in arguments:
|
|
210
|
-
arguments["StartTime"] = int(arguments["StartTime"])
|
|
211
|
-
if "EndTime" in arguments:
|
|
212
|
-
arguments["EndTime"] = int(arguments["EndTime"])
|
|
213
178
|
|
|
214
|
-
|
|
179
|
+
class CTClient(C2Client):
|
|
215
180
|
|
|
216
|
-
|
|
181
|
+
url_key = "AWS_CLOUDTRAIL_URL"
|
|
182
|
+
client_name = "cloudtrail"
|
|
217
183
|
|
|
218
184
|
|
|
219
185
|
class ASClient(C2Client):
|
|
@@ -221,18 +187,6 @@ class ASClient(C2Client):
|
|
|
221
187
|
url_key = "AUTO_SCALING_URL"
|
|
222
188
|
client_name = "autoscaling"
|
|
223
189
|
|
|
224
|
-
@classmethod
|
|
225
|
-
def is_conversion_needed(cls, argument_name: str) -> bool:
|
|
226
|
-
"""Check whether type conversion is needed for argument."""
|
|
227
|
-
|
|
228
|
-
patterns = (
|
|
229
|
-
r"Filters\.\d+\.Values\.\d+",
|
|
230
|
-
)
|
|
231
|
-
for pattern in patterns:
|
|
232
|
-
if re.fullmatch(pattern, argument_name):
|
|
233
|
-
return False
|
|
234
|
-
return True
|
|
235
|
-
|
|
236
190
|
|
|
237
191
|
class BSClient(C2Client):
|
|
238
192
|
|
|
@@ -10,3 +10,13 @@ class EnvironmentVariableError(Exception):
|
|
|
10
10
|
class MalformedParametersError(Exception):
|
|
11
11
|
def __init__(self):
|
|
12
12
|
super(MalformedParametersError, self).__init__("Malformed parameters.")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class InvalidParameterName(Exception):
|
|
16
|
+
def __init__(self, parameter_name: str) -> None:
|
|
17
|
+
super().__init__(f"Parameter with name '{parameter_name}' was not found.")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class InvalidMethodName(Exception):
|
|
21
|
+
def __init__(self, method_name: str) -> None:
|
|
22
|
+
super().__init__(f"Not found method by name: {method_name}.")
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import dataclasses
|
|
2
|
+
import os
|
|
3
|
+
from typing import Any, Dict
|
|
4
|
+
|
|
5
|
+
from botocore.model import ListShape, StructureShape, Shape
|
|
6
|
+
|
|
7
|
+
from c2client.errors import EnvironmentVariableError, MalformedParametersError, InvalidParameterName
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@dataclasses.dataclass
|
|
11
|
+
class Parameter:
|
|
12
|
+
"""API parameter info."""
|
|
13
|
+
|
|
14
|
+
name: str
|
|
15
|
+
shape: Shape
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def from_dot_notation(source):
|
|
19
|
+
"""Convert a incoming query to a request dictionary.
|
|
20
|
+
For example::
|
|
21
|
+
1. {"Action": ["Action"], "Param": ["Value"]}
|
|
22
|
+
2. {"Action": ["Action"], "Param.2": ["Value2"], "Param.1": ["Value1"]}
|
|
23
|
+
3. {"Action": ["Action"], "Param.SubParam": ["Value"]}
|
|
24
|
+
would result in the params dict::
|
|
25
|
+
1. {"Action": "Action", "Param": "Value"}
|
|
26
|
+
2. {"Action": "Action", "Param": ["Value1", "Value2"]}
|
|
27
|
+
3. {"Action": "Action", "Param": { "SubParam": "Value"}}
|
|
28
|
+
:type query: dict
|
|
29
|
+
:param query: This is dictionary, returned by '_get_query()'.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
result = {"result": {}}
|
|
33
|
+
for key, value in sorted(source.items()):
|
|
34
|
+
try:
|
|
35
|
+
_process_tokens(key.split("."), value, result, "result")
|
|
36
|
+
except Exception:
|
|
37
|
+
raise MalformedParametersError
|
|
38
|
+
return result["result"]
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _process_tokens(tokens, value, parent, index):
|
|
42
|
+
key, rest = tokens[0], tokens[1:]
|
|
43
|
+
if key.isdigit():
|
|
44
|
+
key = int(key) - 1
|
|
45
|
+
|
|
46
|
+
if not isinstance(key, int):
|
|
47
|
+
parent[index].setdefault(key, {})
|
|
48
|
+
elif isinstance(parent[index], dict) and not parent[index]:
|
|
49
|
+
parent[index] = [{}]
|
|
50
|
+
elif isinstance(parent[index], list) and len(parent[index]) == key:
|
|
51
|
+
parent[index].append({})
|
|
52
|
+
elif not (isinstance(parent[index], list) and len(parent[index]) > key):
|
|
53
|
+
raise MalformedParametersError
|
|
54
|
+
|
|
55
|
+
if rest:
|
|
56
|
+
_process_tokens(rest, value, parent[index], key)
|
|
57
|
+
else:
|
|
58
|
+
parent[index][key] = value
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def get_env_var(name: str) -> Any:
|
|
62
|
+
"""Return env_var by it's name or raises EnvironmentError."""
|
|
63
|
+
|
|
64
|
+
env_var = os.environ.get(name)
|
|
65
|
+
if env_var is None:
|
|
66
|
+
raise EnvironmentVariableError(name)
|
|
67
|
+
return env_var
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def collect_param_shapes(shape: Shape) -> Dict[str, Parameter]:
|
|
71
|
+
"""Collect a dict of API schema parameters by their name or serialization name."""
|
|
72
|
+
|
|
73
|
+
param_shapes = {}
|
|
74
|
+
for member_name in shape.members:
|
|
75
|
+
param_shapes[member_name.lower()] = Parameter(
|
|
76
|
+
name=member_name, shape=shape.members[member_name])
|
|
77
|
+
if shape.members[member_name].serialization.get("name"):
|
|
78
|
+
param_shapes[shape.members[member_name].serialization.get("name").lower()] = Parameter(
|
|
79
|
+
name=member_name, shape=shape.members[member_name])
|
|
80
|
+
|
|
81
|
+
return param_shapes
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def convert_args(params: Any, shape: Shape):
|
|
85
|
+
"""Convert values in the params dictionary to the types expected by shape."""
|
|
86
|
+
|
|
87
|
+
if not isinstance(shape, StructureShape):
|
|
88
|
+
return convert_arg(value=params, shape=shape)
|
|
89
|
+
|
|
90
|
+
converted_params = {}
|
|
91
|
+
param_shapes = collect_param_shapes(shape)
|
|
92
|
+
|
|
93
|
+
for param_name, param_value in params.items():
|
|
94
|
+
parameter = param_shapes.get(param_name.lower())
|
|
95
|
+
|
|
96
|
+
if not parameter:
|
|
97
|
+
raise InvalidParameterName(param_name)
|
|
98
|
+
|
|
99
|
+
if parameter.shape:
|
|
100
|
+
converted_params[parameter.name] = convert_arg(param_value, parameter.shape)
|
|
101
|
+
else:
|
|
102
|
+
converted_params[parameter.name] = param_value
|
|
103
|
+
|
|
104
|
+
return converted_params
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def convert_arg(value: Any, shape: Shape):
|
|
108
|
+
"""Convert an individual value to the type expected by shape."""
|
|
109
|
+
|
|
110
|
+
if isinstance(shape, ListShape):
|
|
111
|
+
if not isinstance(value, list):
|
|
112
|
+
raise ValueError(f"Expected list for {shape.name}, got {type(value).__name__}")
|
|
113
|
+
return [convert_arg(v, shape.member) for v in value]
|
|
114
|
+
|
|
115
|
+
elif isinstance(shape, StructureShape):
|
|
116
|
+
if not isinstance(value, dict):
|
|
117
|
+
raise ValueError(f"Expected dict for {shape.name}, got {type(value).__name__}")
|
|
118
|
+
return convert_args(value, shape)
|
|
119
|
+
|
|
120
|
+
elif shape.type_name == "string":
|
|
121
|
+
return str(value)
|
|
122
|
+
|
|
123
|
+
elif shape.type_name == "integer" or shape.type_name == "long":
|
|
124
|
+
return int(value)
|
|
125
|
+
|
|
126
|
+
elif shape.type_name == "float" or shape.type_name == "double":
|
|
127
|
+
return float(value)
|
|
128
|
+
|
|
129
|
+
elif shape.type_name == "boolean":
|
|
130
|
+
if isinstance(value, str) and value.lower() == "false":
|
|
131
|
+
return False
|
|
132
|
+
return bool(value)
|
|
133
|
+
else:
|
|
134
|
+
return value
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: c2client
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.24
|
|
4
4
|
Summary: CROC Cloud Platform - API Client
|
|
5
5
|
Home-page: https://github.com/c2devel/c2-client
|
|
6
6
|
Author: CROC Cloud Team
|
|
@@ -8,12 +8,20 @@ Author-email: devel@croc.ru
|
|
|
8
8
|
Maintainer: Andrey Kulaev
|
|
9
9
|
Maintainer-email: adkulaev@gmail.com
|
|
10
10
|
License: GPL3
|
|
11
|
-
|
|
11
|
+
Platform: UNKNOWN
|
|
12
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
12
13
|
Classifier: Environment :: Console
|
|
13
14
|
Classifier: Intended Audience :: Developers
|
|
14
15
|
Classifier: Intended Audience :: System Administrators
|
|
15
16
|
Classifier: Operating System :: OS Independent
|
|
16
17
|
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.6
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.7
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
25
|
|
|
18
26
|
CROC Cloud API Client
|
|
19
27
|
=====================
|
|
@@ -21,7 +29,7 @@ CROC Cloud API Client
|
|
|
21
29
|
Simple command-line utility for sending custom requests to CROC Cloud platform.
|
|
22
30
|
|
|
23
31
|
**Warning: this utility is not intended for automation cases.
|
|
24
|
-
Use https://github.com/c2devel/
|
|
32
|
+
Use https://github.com/c2devel/boto3.git and python scripts instead.**
|
|
25
33
|
|
|
26
34
|
Installation
|
|
27
35
|
------------
|
|
@@ -66,3 +74,5 @@ Send simple request:
|
|
|
66
74
|
|
|
67
75
|
$ c2-ec2 RunInstances ImageId cmi-078880A0 Description "Test instance" \
|
|
68
76
|
InstanceType m1.small MaxCount 1 MinCount 1 SecurityGroup.1 test
|
|
77
|
+
|
|
78
|
+
|
|
@@ -14,11 +14,9 @@ def get_description():
|
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
install_requires = [
|
|
17
|
-
"boto",
|
|
18
17
|
"boto3",
|
|
19
18
|
"botocore",
|
|
20
19
|
"inflection==0.3.1",
|
|
21
|
-
"lxml",
|
|
22
20
|
]
|
|
23
21
|
|
|
24
22
|
entrypoints = [
|
|
@@ -49,12 +47,19 @@ setup(
|
|
|
49
47
|
maintainer="Andrey Kulaev",
|
|
50
48
|
maintainer_email="adkulaev@gmail.com",
|
|
51
49
|
classifiers=[
|
|
52
|
-
"Development Status ::
|
|
50
|
+
"Development Status :: 5 - Production/Stable",
|
|
53
51
|
"Environment :: Console",
|
|
54
52
|
"Intended Audience :: Developers",
|
|
55
53
|
"Intended Audience :: System Administrators",
|
|
56
54
|
"Operating System :: OS Independent",
|
|
57
55
|
"Programming Language :: Python :: 3",
|
|
56
|
+
"Programming Language :: Python :: 3.6",
|
|
57
|
+
"Programming Language :: Python :: 3.7",
|
|
58
|
+
"Programming Language :: Python :: 3.8",
|
|
59
|
+
"Programming Language :: Python :: 3.9",
|
|
60
|
+
"Programming Language :: Python :: 3.10",
|
|
61
|
+
"Programming Language :: Python :: 3.11",
|
|
62
|
+
"Programming Language :: Python :: 3.12",
|
|
58
63
|
],
|
|
59
64
|
install_requires=install_requires,
|
|
60
65
|
packages=find_packages(),
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.22"
|
c2client-0.22/c2client/utils.py
DELETED
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import os
|
|
2
|
-
|
|
3
|
-
from lxml import etree
|
|
4
|
-
|
|
5
|
-
from c2client.errors import EnvironmentVariableError, MalformedParametersError
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
def prettify_xml(string):
|
|
9
|
-
"""Returns prettified XML string."""
|
|
10
|
-
|
|
11
|
-
parser = etree.XMLParser(remove_blank_text=True)
|
|
12
|
-
tree = etree.fromstring(string, parser)
|
|
13
|
-
return etree.tostring(tree, pretty_print=True, encoding="unicode")
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
def from_dot_notation(source):
|
|
17
|
-
"""Converts a incoming query to a request dictionary.
|
|
18
|
-
For example::
|
|
19
|
-
1. {"Action": ["Action"], "Param": ["Value"]}
|
|
20
|
-
2. {"Action": ["Action"], "Param.2": ["Value2"], "Param.1": ["Value1"]}
|
|
21
|
-
3. {"Action": ["Action"], "Param.SubParam": ["Value"]}
|
|
22
|
-
would result in the params dict::
|
|
23
|
-
1. {"Action": "Action", "Param": "Value"}
|
|
24
|
-
2. {"Action": "Action", "Param": ["Value1", "Value2"]}
|
|
25
|
-
3. {"Action": "Action", "Param": { "SubParam": "Value"}}
|
|
26
|
-
:type query: dict
|
|
27
|
-
:param query: This is dictionary, returned by '_get_query()'.
|
|
28
|
-
"""
|
|
29
|
-
result = {"result": {}}
|
|
30
|
-
for key, value in sorted(source.items()):
|
|
31
|
-
try:
|
|
32
|
-
_process_tokens(key.split("."), value, result, "result")
|
|
33
|
-
except Exception:
|
|
34
|
-
raise MalformedParametersError
|
|
35
|
-
return result["result"]
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
def _process_tokens(tokens, value, parent, index):
|
|
39
|
-
key, rest = tokens[0], tokens[1:]
|
|
40
|
-
if key.isdigit():
|
|
41
|
-
key = int(key) - 1
|
|
42
|
-
|
|
43
|
-
if not isinstance(key, int):
|
|
44
|
-
parent[index].setdefault(key, {})
|
|
45
|
-
elif isinstance(parent[index], dict) and not parent[index]:
|
|
46
|
-
parent[index] = [{}]
|
|
47
|
-
elif isinstance(parent[index], list) and len(parent[index]) == key:
|
|
48
|
-
parent[index].append({})
|
|
49
|
-
elif not (isinstance(parent[index], list) and len(parent[index]) > key):
|
|
50
|
-
raise MalformedParametersError
|
|
51
|
-
|
|
52
|
-
if rest:
|
|
53
|
-
_process_tokens(rest, value, parent[index], key)
|
|
54
|
-
else:
|
|
55
|
-
parent[index][key] = value
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
def get_env_var(name):
|
|
59
|
-
"""Returns env_var by it's name or raises EnvironmentError."""
|
|
60
|
-
|
|
61
|
-
env_var = os.environ.get(name)
|
|
62
|
-
if env_var is None:
|
|
63
|
-
raise EnvironmentVariableError(name)
|
|
64
|
-
return env_var
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|