diff --git a/README.md b/README.md index 5a8dc96..5e2b8eb 100644 --- a/README.md +++ b/README.md @@ -17,4 +17,4 @@ This client can run on Linux, macOS and Windows. - Website: https://www.ucloud.cn/ - Free software: Apache 2.0 license -- [Documentation](https://ucloud.github.io/ucloud-sdk-python2/) +- [Documentation](https://docs.ucloud.cn/opensdk-python/) diff --git a/examples/auth/README.md b/examples/auth/README.md new file mode 100644 index 0000000..b5cbf07 --- /dev/null +++ b/examples/auth/README.md @@ -0,0 +1,15 @@ +# UCloud SDK Auth Example + +## What is the goal + +Create signature from request payload. + +## Setup Environment + +Don't need. + +## How to run + +```sh +python main.py +``` diff --git a/examples/auth/__init__.py b/examples/auth/__init__.py new file mode 100644 index 0000000..40a96af --- /dev/null +++ b/examples/auth/__init__.py @@ -0,0 +1 @@ +# -*- coding: utf-8 -*- diff --git a/examples/auth/main.py b/examples/auth/main.py new file mode 100644 index 0000000..b073841 --- /dev/null +++ b/examples/auth/main.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + +from ucloud.core import auth + + +def main(): + cred = auth.Credential( + "ucloudsomeone@example.com1296235120854146120", + "46f09bb9fab4f12dfc160dae12273d5332b5debe", + ) + d = {"Action": "DescribeUHostInstance", "Region": "cn-bj2", "Limit": 10} + print(cred.verify_ac(d)) + + +if __name__ == "__main__": + main() diff --git a/tests/test_unit/test_core/consts.py b/tests/test_unit/test_core/consts.py new file mode 100644 index 0000000..04ad3a2 --- /dev/null +++ b/tests/test_unit/test_core/consts.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- + +TEST_URL = "https://api.ucloud.cn/" diff --git a/tests/test_unit/test_core/test_auth.py b/tests/test_unit/test_core/test_auth.py index 4128ff3..411c8bd 100644 --- a/tests/test_unit/test_core/test_auth.py +++ b/tests/test_unit/test_core/test_auth.py @@ -24,3 +24,7 @@ def test_verify_ac(): "46f09bb9fab4f12dfc160dae12273d5332b5debe", ) assert cred.verify_ac(d) == "4f9ef5df2abab2c6fccd1e9515cb7e2df8c6bb65" + assert cred.to_dict() == { + "public_key": "ucloudsomeone@example.com1296235120854146120", + "private_key": "46f09bb9fab4f12dfc160dae12273d5332b5debe", + } diff --git a/tests/test_unit/test_core/test_client.py b/tests/test_unit/test_core/test_client.py index 4549389..908d947 100644 --- a/tests/test_unit/test_core/test_client.py +++ b/tests/test_unit/test_core/test_client.py @@ -1,16 +1,21 @@ # -*- coding: utf-8 -*- -import os +import json +import uuid import pytest import logging +import collections +import requests_mock from ucloud.client import Client from ucloud.core import exc +from ucloud.core.transport import RequestsTransport, http from ucloud.testing.mock import MockedTransport +from tests.test_unit.test_core import consts logger = logging.getLogger(__name__) -@pytest.fixture(scope="session", autouse=True) +@pytest.fixture(scope="function", autouse=True) def client(): return Client( { @@ -29,49 +34,68 @@ def transport(): return MockedTransport() -class TestClient(object): - def test_client_invoke(self, client, transport): - transport.mock_data(lambda _: {"RetCode": 0, "Action": "Foo"}) - client.transport = transport - assert client.invoke("Foo") == {"RetCode": 0, "Action": "Foo"} +def test_client_invoke(client): + expected = {"RetCode": 0, "Action": "Foo"} + with requests_mock.Mocker() as m: + m.post( + consts.TEST_URL, + text=json.dumps(expected), + headers={http.REQUEST_UUID_HEADER_KEY: str(uuid.uuid4())}, + ) + assert client.invoke("Foo") == expected + - def test_client_invoke_code_error(self, client, transport): - transport.mock_data(lambda _: {"RetCode": 171, "Action": "Foo"}) - client.transport = transport +def test_client_invoke_code_error(client): + expected = {"RetCode": 171, "Action": "Foo", "Message": "签名错误"} + with requests_mock.Mocker() as m: + m.post( + consts.TEST_URL, + text=json.dumps(expected), + headers={http.REQUEST_UUID_HEADER_KEY: str(uuid.uuid4())}, + ) with pytest.raises(exc.RetCodeException): try: client.invoke("Foo") except exc.RetCodeException as e: - assert str(e) - expected = {"RetCode": 171, "Action": "Foo", "Message": ""} - assert e.json() == expected + assert e.retryable is False + assert e.json() == { + "RetCode": 171, + "Action": "Foo", + "Message": "签名错误", + } raise e - def test_client_invoke_with_retryable_error(self, client, transport): - transport.mock_data(lambda _: {"RetCode": 10000, "Action": "Foo"}) - client.transport = transport + +def test_client_invoke_with_retryable_error(client): + with requests_mock.Mocker() as m: + m.post( + consts.TEST_URL, + text=json.dumps({"RetCode": 10000, "Action": "Foo"}), + ) with pytest.raises(exc.RetCodeException): client.invoke("Foo") - def test_client_invoke_with_unexpected_error(self, client, transport): - def raise_error(_): - raise ValueError("temporary error") - transport.mock_data(raise_error) - client.transport = transport - with pytest.raises(ValueError): - client.invoke("Foo") +def test_client_invoke_with_unexpected_error(client): + def raise_error(_): + raise ValueError("temporary error") + + transport = RequestsTransport() + transport.middleware.request(raise_error) + client.transport = transport + with pytest.raises(ValueError): + client.invoke("Foo") + - def test_client_try_import(self, client): - assert client.pathx() - assert client.stepflow() - assert client.uaccount() - assert client.udb() - assert client.udpn() - assert client.udisk() - assert client.uhost() - assert client.ulb() - assert client.umem() - assert client.unet() - assert client.uphost() - assert client.vpc() +def test_client_try_import(client): + for name in dir(client): + if name.startswith("_") or name in [ + "invoke", + "logged_request_handler", + "logged_response_handler", + "logged_exception_handler", + ]: + continue + client_factory = getattr(client, name) + if isinstance(client_factory, collections.Callable): + print(client_factory()) diff --git a/tests/test_unit/test_core/test_transport.py b/tests/test_unit/test_core/test_transport.py index ec519fb..69ff658 100644 --- a/tests/test_unit/test_core/test_transport.py +++ b/tests/test_unit/test_core/test_transport.py @@ -1,72 +1,129 @@ # -*- coding: utf-8 -*- +import json +import uuid import pytest import logging -from ucloud.core.transport import RequestsTransport, Request, Response, utils +import requests_mock +from collections import Counter +from tests.test_unit.test_core.consts import TEST_URL +from ucloud.core import exc +from ucloud.core.transport import ( + RequestsTransport, + Request, + Response, + utils, + http, +) logger = logging.getLogger(__name__) -@pytest.fixture(scope="function", autouse=True) -def transport(): +@pytest.fixture(name="transport", scope="function", autouse=True) +def transport_factory(): return RequestsTransport() -class TestTransport(object): - def test_transport_send(self, transport): - req = Request( - url="http://httpbin.org/anything", - method="post", - json={"foo": "bar"}, - ) - resp = transport.send(req) - assert resp.text - assert resp.json()["json"] == {"foo": "bar"} +@pytest.mark.parametrize( + argnames=("status_code", "content", "expect", "expect_exc", "retryable"), + argvalues=( + ( + 200, + '{"Action": "Mock", "RetCode": 0}', + {"Action": "Mock", "RetCode": 0}, + None, + False, + ), + (500, "{}", None, exc.HTTPStatusException, False), + (429, "{}", None, exc.HTTPStatusException, True), + (500, "x", None, exc.HTTPStatusException, False), + (200, "x", None, exc.InvalidResponseException, False), + ), +) +def test_transport( + transport, status_code, content, expect, expect_exc, retryable +): + with requests_mock.Mocker() as m: + m.post(TEST_URL, text=content, status_code=status_code) + got_exc = None + try: + resp = transport.send(Request(url=TEST_URL, method="post", json={})) + assert resp.json() == expect + except Exception as e: + got_exc = e + if expect_exc: + assert str(got_exc) + assert got_exc.retryable == retryable + assert isinstance(got_exc, expect_exc) - def test_transport_handler(self, transport): - global_env = {} - def request_handler(r): - global_env["req"] = r - return r +def test_transport_handler(transport): + req_key, resp_key, exc_key = "req", "resp", "exc" + counter = Counter({req_key: 0, resp_key: 0, exc_key: 0}) - def response_handler(r): - global_env["resp"] = r - return r + def request_handler(r): + counter[req_key] += 1 + return r - transport.middleware.request(handler=request_handler) - transport.middleware.response(handler=response_handler) - req = Request( - url="http://httpbin.org/anything", - method="post", - json={"foo": "bar"}, + def response_handler(r): + counter[resp_key] += 1 + return r + + def exception_handler(r): + counter[exc_key] += 1 + return r + + transport.middleware.request(handler=request_handler) + transport.middleware.response(handler=response_handler) + transport.middleware.exception(handler=exception_handler) + expect = {"foo": "bar"} + req = Request(url=TEST_URL, method="post", json=expect) + with requests_mock.Mocker() as m: + request_uuid = str(uuid.uuid4()) + m.post( + TEST_URL, + text=json.dumps(expect), + status_code=200, + headers={http.REQUEST_UUID_HEADER_KEY: request_uuid}, ) resp = transport.send(req) assert resp.text - assert resp.json()["json"] == {"foo": "bar"} - assert "req" in global_env - assert "resp" in global_env - - -class TestResponse(object): - def test_guess_json_utf(self): - import json - - encodings = [ - "utf-32", - "utf-8-sig", - "utf-16", - "utf-8", - "utf-16-be", - "utf-16-le", - "utf-32-be", - "utf-32-le", - ] - for e in encodings: - s = json.dumps("表意字符").encode(e) - assert utils.guess_json_utf(s) == e - - def test_response_empty_content(self): - r = Response("http://foo.bar", "post") - assert not r.text + assert resp.json() == expect + assert resp.request_uuid == request_uuid + with pytest.raises(Exception): + transport.send(Request(url="/")) + assert counter[req_key] == 2 + assert counter[resp_key] == 1 + assert counter[exc_key] == 1 + + +def test_guess_json_utf(): + encodings = [ + "utf-32", + "utf-8-sig", + "utf-16", + "utf-8", + "utf-16-be", + "utf-16-le", + "utf-32-be", + "utf-32-le", + ] + for e in encodings: + s = json.dumps("表意字符").encode(e) + assert utils.guess_json_utf(s) == e + + +def test_request_methods(): + req = Request( + TEST_URL, data={"foo": 42}, json={"bar": 42}, params={"q": "search"} + ) + assert req.payload() == {"foo": 42, "bar": 42, "q": "search"} + + +def test_response_methods(): + r = Response(TEST_URL, "post") + assert not r.text + assert r.json() is None + r = Response(TEST_URL, "post", content=b"\xd6", encoding="utf-8") + with pytest.raises(exc.InvalidResponseException): assert r.json() is None diff --git a/ucloud/client.py b/ucloud/client.py index a9e9187..caa97c4 100644 --- a/ucloud/client.py +++ b/ucloud/client.py @@ -1,13 +1,13 @@ # -*- coding: utf-8 -*- """ Code is generated by ucloud-model, DO NOT EDIT IT. """ -from ucloud._compat import CompactClient +from ucloud.core import client -class Client(CompactClient): - def __init__(self, config, transport=None, middleware=None): +class Client(client.Client): + def __init__(self, config, transport=None, middleware=None, logger=None): self._config = config - super(Client, self).__init__(config, transport, middleware) + super(Client, self).__init__(config, transport, middleware, logger) def pathx(self): from ucloud.services.pathx.client import PathXClient diff --git a/ucloud/core/client/_client.py b/ucloud/core/client/_client.py index bed8035..f9b3ea7 100644 --- a/ucloud/core/client/_client.py +++ b/ucloud/core/client/_client.py @@ -9,26 +9,26 @@ RequestsTransport, Request, SSLOption, + http, ) from ucloud.core.typesystem import encoder from ucloud.core.utils import log from ucloud.core.utils.middleware import Middleware from ucloud.core import auth, exc -default_transport = RequestsTransport() - class Client(object): def __init__(self, config, transport=None, middleware=None, logger=None): cfg, cred = self._parse_dict_config(config) self.config = cfg self.credential = cred - self.transport = transport or default_transport + self.transport = transport or RequestsTransport() self.logger = logger or log.default_logger if middleware is None: middleware = Middleware() middleware.response(self.logged_response_handler) middleware.request(self.logged_request_handler) + middleware.exception(self.logged_exception_handler) self._middleware = middleware def invoke(self, action, args=None, **options): @@ -40,12 +40,13 @@ def invoke(self, action, args=None, **options): """ retries = 0 max_retries = options.get("max_retries") or self.config.max_retries + timeout = options.get("timeout") or self.config.timeout while retries <= max_retries: try: - return self._send(action, args or {}, **options) + return self._send( + action, args or {}, max_retries=max_retries, timeout=timeout + ) except exc.UCloudException as e: - for handler in self.middleware.exception_handlers: - handler(e) if e.retryable and retries != max_retries: logging.info( "Retrying {action}: {args}".format( @@ -55,56 +56,70 @@ def invoke(self, action, args=None, **options): retries += 1 continue raise e - except Exception as e: - for handler in self.middleware.exception_handlers: - handler(e) - raise e @property def middleware(self): return self._middleware def logged_request_handler(self, req): - self.logger.info("[request] {} {}".format(req.get("Action", ""), req)) + action = req.get("Action", "") + self.logger.info("[request] {} {}".format(action, req)) return req - def logged_response_handler(self, resp): + def logged_response_handler(self, resp, http_resp=None): + action = resp.get("Action", "") + request_uuid = http_resp and http_resp.request_uuid self.logger.info( - "[response] {} {}".format(resp.get("Action", ""), resp) + "[response] [{}] {} {}".format(request_uuid or "*", action, resp) ) return resp + def logged_exception_handler(self, e): + if isinstance(e, exc.RetCodeException): + self.logger.warning(e) + else: + self.logger.exception(e) + return e + @staticmethod def _parse_dict_config(config): return Config.from_dict(config), auth.Credential.from_dict(config) - def _send(self, action, args, **options): + def _send(self, action, args, max_retries, timeout): args["Action"] = action for handler in self.middleware.request_handlers: args = handler(args) - req = self._build_http_request(args) - max_retries = options.get("max_retries") or self.config.max_retries - timeout = options.get("timeout") or self.config.timeout - resp = self.transport.send( - req, - ssl_option=SSLOption( - self.config.ssl_verify, - self.config.ssl_cacert, - self.config.ssl_cert, - self.config.ssl_key, - ), - timeout=timeout, - max_retries=max_retries, - ).json() - for handler in self.middleware.response_handlers: - resp = handler(resp) - if int(resp.get("RetCode", -1)) != 0: - raise exc.RetCodeException( - action=req.data.get("Action"), - code=int(resp.get("RetCode")), - message=resp.get("Message"), + try: + req = self._build_http_request(args) + resp = self.transport.send( + req, + ssl_option=SSLOption( + self.config.ssl_verify, + self.config.ssl_cacert, + self.config.ssl_cert, + self.config.ssl_key, + ), + timeout=timeout, + max_retries=max_retries, ) - return resp + data = resp.json() + except Exception as e: + for handler in self.middleware.exception_handlers: + handler(e) + raise e + for handler in self.middleware.response_handlers: + data = handler(data, resp) + if int(data.get("RetCode", -1)) == 0: + return data + ret_code_exc = exc.RetCodeException( + action=req.data.get("Action", ""), + code=int(data.get("RetCode", 0)), + message=data.get("Message", ""), + request_uuid=resp.request_uuid, + ) + for handler in self.middleware.exception_handlers: + handler(ret_code_exc) + raise ret_code_exc def _build_http_request(self, args): config = { diff --git a/ucloud/core/exc/__init__.py b/ucloud/core/exc/__init__.py index 65bf867..bdf3185 100644 --- a/ucloud/core/exc/__init__.py +++ b/ucloud/core/exc/__init__.py @@ -5,11 +5,7 @@ ValidationException, RetCodeException, RetryTimeoutException, + TransportException, + HTTPStatusException, + InvalidResponseException, ) - -__all__ = [ - "UCloudException", - "ValidationException", - "RetCodeException", - "RetryTimeoutException", -] diff --git a/ucloud/core/exc/_exc.py b/ucloud/core/exc/_exc.py index 7c8af49..d9e7795 100644 --- a/ucloud/core/exc/_exc.py +++ b/ucloud/core/exc/_exc.py @@ -13,18 +13,56 @@ def retryable(self): MAX_COMMON_RET_CODE = 2000 +class TransportException(UCloudException): + pass + + +class HTTPStatusException(TransportException): + def __init__(self, status_code, request_uuid=None): + self.status_code = status_code + self.request_uuid = request_uuid + + @property + def retryable(self): + return self.status_code in [429, 502, 503, 504] + + def __str__(self): + return "[{uuid}] {self.status_code} http status error".format( + self=self, uuid=self.request_uuid or "*" + ) + + +class InvalidResponseException(TransportException): + def __init__(self, content, message, request_uuid=None): + self.content = content + self.message = message + self.request_uuid = request_uuid + + @property + def retryable(self): + return False + + def __str__(self): + return "[{uuid}] {self.message}: {self.content}".format( + self=self, uuid=self.request_uuid or "*" + ) + + class RetCodeException(UCloudException): - def __init__(self, action, code, message): + def __init__(self, action, code, message, request_uuid=None): self.action = action self.code = code self.message = message + self.request_uuid = request_uuid @property def retryable(self): return self.code > MAX_COMMON_RET_CODE def __str__(self): - return "{self.action} - {self.code}: {self.message}".format(self=self) + return "[{uuid}] {self.action} - {self.code}: {self.message}".format( + self=self, uuid=self.request_uuid or "*" + ) def json(self): return { diff --git a/ucloud/core/transport/_requests.py b/ucloud/core/transport/_requests.py index 483f258..03b6c50 100644 --- a/ucloud/core/transport/_requests.py +++ b/ucloud/core/transport/_requests.py @@ -7,6 +7,7 @@ from ucloud.core.transport import http from ucloud.core.transport.http import Request, Response, SSLOption from ucloud.core.utils.middleware import Middleware +from ucloud.core import exc class RequestsTransport(http.Transport): @@ -79,6 +80,10 @@ def _send(self, req, **options): resp = self.convert_response(session_resp) resp.request = req resp.response_time = time.time() + if resp.status_code >= 400: + raise exc.HTTPStatusException( + resp.status_code, resp.request_uuid + ) return resp @staticmethod diff --git a/ucloud/core/transport/http.py b/ucloud/core/transport/http.py index 730c977..ec92062 100644 --- a/ucloud/core/transport/http.py +++ b/ucloud/core/transport/http.py @@ -2,6 +2,7 @@ import logging import json as json_mod +from ucloud.core import exc from ucloud.core.transport import utils from ucloud.core.utils.compat import str @@ -34,6 +35,9 @@ def payload(self): return payload +REQUEST_UUID_HEADER_KEY = "X-UCLOUD-REQUEST-UUID" + + class Response(object): def __init__( self, @@ -52,23 +56,23 @@ def __init__( self.request = request self.status_code = status_code self.reason = reason - self.headers = headers self.content = content self.encoding = encoding self.response_time = 0 + self.headers = headers or {} + self.request_uuid = self.headers.get(REQUEST_UUID_HEADER_KEY) def json(self, **kwargs): """ json will return the bytes of content """ if not self.content: return None - encoding = utils.guess_json_utf(self.content) - if encoding is not None: - try: - return json_mod.loads(self.content.decode(encoding), **kwargs) - except UnicodeDecodeError: - pass - return json_mod.loads(self.text, **kwargs) + try: + return self._decode_json(**kwargs) + except Exception as e: + raise exc.InvalidResponseException( + self.content, str(e), request_uuid=self.request_uuid + ) @property def text(self): @@ -83,6 +87,15 @@ def text(self): content = str(self.content, errors="replace") return content + def _decode_json(self, **kwargs): + encoding = utils.guess_json_utf(self.content) + if encoding is not None: + try: + return json_mod.loads(self.content.decode(encoding), **kwargs) + except UnicodeDecodeError: + pass + return json_mod.loads(self.text, **kwargs) + class SSLOption(object): def __init__( diff --git a/ucloud/services/ucloudstack/client.py b/ucloud/services/ucloudstack/client.py index ac3591d..8f456fd 100644 --- a/ucloud/services/ucloudstack/client.py +++ b/ucloud/services/ucloudstack/client.py @@ -22,6 +22,8 @@ def allocate_eip(self, req=None, **kwargs): - **Name** (str) - (Required) 名称 - **OperatorName** (str) - (Required) 线路。目前支持Bgp - **Zone** (str) - (Required) 可用区。枚举值:zone-01,表示中国; + - **IP** (str) - 指定IP + - **IPVersion** (str) - IP版本,默认值IPv4,支持值:IPv4\\IPv6 - **Quantity** (int) - 购买时长。默认值1。小时不生效,月范围【1,11】,年范围【1,5】。 **Response** @@ -58,6 +60,27 @@ def attach_disk(self, req=None, **kwargs): resp = self.invoke("AttachDisk", d, **kwargs) return apis.AttachDiskResponseSchema().loads(resp) + def attach_nic(self, req=None, **kwargs): + """ AttachNIC - 绑定UCloudStack网卡 + + **Request** + + - **Region** (str) - (Config) 地域。枚举值:cn,表示中国; + - **NICID** (str) - (Required) 网卡ID + - **ResourceID** (str) - (Required) 绑定的资源ID + - **Zone** (str) - (Required) 可用区。枚举值:zone-01,表示中国; + + **Response** + + - **Message** (str) - 返回信息描述。 + + """ + d = {"Region": self.config.region} + req and d.update(req) + d = apis.AttachNICRequestSchema().dumps(d) + resp = self.invoke("AttachNIC", d, **kwargs) + return apis.AttachNICResponseSchema().loads(resp) + def bind_alarm_template(self, req=None, **kwargs): """ BindAlarmTemplate - 绑定告警模板 @@ -81,7 +104,7 @@ def bind_alarm_template(self, req=None, **kwargs): return apis.BindAlarmTemplateResponseSchema().loads(resp) def bind_eip(self, req=None, **kwargs): - """ BindEIP - 绑定UCoudStack外网IP + """ BindEIP - 绑定外网 IP **Request** @@ -133,7 +156,7 @@ def bind_security_group(self, req=None, **kwargs): - **ResourceID** (str) - (Required) 绑定的资源ID。调用方式举例:ResourceID=“one-id”。 - **SGID** (str) - (Required) 安全组ID - **Zone** (str) - (Required) 可用区。枚举值:zone-01,表示中国; - - **NICID** (str) - 网卡ID + - **NICType** (str) - 网卡类型,玫举值:WAN,外网;LAN,内网,默认为WAN **Response** @@ -325,6 +348,31 @@ def create_natgw_rule(self, req=None, **kwargs): resp = self.invoke("CreateNATGWRule", d, **kwargs) return apis.CreateNATGWRuleResponseSchema().loads(resp) + def create_nic(self, req=None, **kwargs): + """ CreateNIC - 创建网卡 + + **Request** + + - **Region** (str) - (Config) 地域。枚举值:cn,表示中国; + - **Name** (str) - (Required) 名称 + - **SubnetID** (str) - (Required) Subnet ID + - **VPCID** (str) - (Required) VPC ID + - **Zone** (str) - (Required) 可用区。枚举值:zone-01,表示中国; + - **IP** (str) - 指定IP + - **SGID** (str) - 安全组 ID + + **Response** + + - **Message** (str) - 返回信息描述。 + - **NICID** (str) - 创建的网卡 ID + + """ + d = {"Region": self.config.region} + req and d.update(req) + d = apis.CreateNICRequestSchema().dumps(d) + resp = self.invoke("CreateNIC", d, **kwargs) + return apis.CreateNICResponseSchema().loads(resp) + def create_physical_ip(self, req=None, **kwargs): """ CreatePhysicalIP - 创建物理 IP ,需确保平台已配置物理 IP 线路相关信息及物理网络联通性。 @@ -490,7 +538,7 @@ def create_vm_instance(self, req=None, **kwargs): **Request** - - **Region** (str) - (Config) 地域。枚举值:cn,表示中国; + - **Region** (str) - (Config) 地域或数据中心。枚举值:cn,表示中国; - **BootDiskSetType** (str) - (Required) 系统盘类型。枚举值:Normal,表示普通;SSD,表示SSD; - **CPU** (int) - (Required) CPU个数,如1,2,4,8,16,32,64等。 - **ChargeType** (str) - (Required) 计费模式。枚举值:Dynamic,表示小时;Month,表示月;Year,表示年; @@ -499,27 +547,27 @@ def create_vm_instance(self, req=None, **kwargs): - **Memory** (int) - (Required) 内存容量,如1024,2048,4096,8192,16384,32768,65535等。 - **Name** (str) - (Required) 虚拟机名称。可输入如:myVM。名称只能包含中英文、数字以及- _ .且1-30个字符。 - **Password** (str) - (Required) 密码。可输入如:ucloud.cn。密码长度限6-30个字符;需要同时包含两项或以上(大写字母/小写字母/数字/特殊符号);windows不能包含用户名(administrator)中超过2个连续字符的部分。 - - **SubnetID** (str) - (Required) 子网 ID。 - - **VMType** (str) - (Required) 机型。枚举值:Normal,表示普通;SSD,表示SSD; - - **VPCID** (str) - (Required) VPC ID。 + - **SubnetID** (str) - (Required) 虚拟机所属子网 ID。 + - **VMType** (str) - (Required) 虚拟机所在宿主机的集群类型,代表不同架构、不同型号的 CPU 或硬件特征。枚举值:Normal,表示普通;SSD,表示SSD。 + - **VPCID** (str) - (Required) 虚拟机所属 VPC ID。 - **WANSGID** (str) - (Required) 外网安全组 ID。输入“有效”状态的安全组的ID。 - **Zone** (str) - (Required) 可用区。枚举值:zone-01,表示中国; - - **Bandwidth** (str) - 带宽 + - **Bandwidth** (str) - 创建虚拟机同时绑定外网 IP 的带宽。 - **DataDiskSpace** (int) - 数据盘大小,单位 GB。默认值为0。范围:【0,8000】,步长10。 - **GPU** (int) - GPU 卡核心的占用个数。枚举值:【1,2,4】。GPU与CPU、内存大小关系:CPU个数>=4*GPU个数,同时内存与CPU规格匹配. - - **IPVersion** (str) - 外网IP版本,默认IPv4 - - **InternalIP** (str) - 指定内网IP。输入有效的指定内网 IP。默认为系统自动分配内网 IP。 - - **InternetIP** (str) - 指定外网IP + - **IPVersion** (str) - 创建虚拟机同时绑定外网 IP 的 IP 版本。枚举值:IPv4 & IPv6,默认为 IPv4 + - **InternalIP** (str) - 指定内网IP。输入有效的指定内网 IP,不指定时系统将自动从子网分配 IP 地址。 + - **InternetIP** (str) - 手动指定虚拟机绑定外网 IP 的地址,IP地址必须包含在网段内。 - **LANSGID** (str) - 内网安全组 ID。输入“有效”状态的安全组的ID。 - - **OperatorName** (str) - 线路 + - **OperatorName** (str) - 创建虚拟机同时绑定外网 IP 的网段,可由管理员自定义。 - **Quantity** (int) - 购买时长。默认值1。小时不生效,月范围【1,11】,年范围【1,5】。 **Response** - - **DiskID** (str) - 返回创建数据盘的 ID - - **EIPID** (str) - 返回创建外网IP的 ID + - **DiskID** (str) - 返回同时创建的数据盘 ID + - **EIPID** (str) - 返回同时创建的外网IP ID - **Message** (str) - 返回信息描述。 - - **VMID** (str) - 返回创建虚拟机的 ID + - **VMID** (str) - 返回创建的虚拟机 ID """ d = {"Region": self.config.region} @@ -574,7 +622,9 @@ def create_vs(self, req=None, **kwargs): **Response** + - **Action** (str) - 操作名称 - **Message** (str) - 返回信息描述。 + - **RetCode** (int) - 返回码 - **VSID** (str) - 返回创建的VSID """ @@ -730,6 +780,26 @@ def delete_natgw_rule(self, req=None, **kwargs): resp = self.invoke("DeleteNATGWRule", d, **kwargs) return apis.DeleteNATGWRuleResponseSchema().loads(resp) + def delete_nic(self, req=None, **kwargs): + """ DeleteNIC - 删除网卡 + + **Request** + + - **Region** (str) - (Config) 地域。枚举值:cn,表示中国; + - **NICID** (str) - (Required) 被删除的网卡 ID + - **Zone** (str) - (Required) 可用区。枚举值:zone-01,表示中国; + + **Response** + + - **Message** (str) - 返回信息描述。 + + """ + d = {"Region": self.config.region} + req and d.update(req) + d = apis.DeleteNICRequestSchema().dumps(d) + resp = self.invoke("DeleteNIC", d, **kwargs) + return apis.DeleteNICResponseSchema().loads(resp) + def delete_physical_ip(self, req=None, **kwargs): """ DeletePhysicalIP - 删除物理IP @@ -996,6 +1066,7 @@ def describe_disk(self, req=None, **kwargs): - **Region** (str) - (Config) 地域。枚举值: cn,表示中国; - **Zone** (str) - (Required) 可用区。枚举值:zone-01,表示中国; - **DiskIDs** (list) - 【数组】磁盘的 ID。输入有效的 ID。调用方式举例:DiskIDs.0=“one-id”、DiskIDs.1=“two-id”。 + - **DiskType** (str) - 硬盘用途类型,默认空返回虚拟机所有硬盘,支持值:Boot(系统盘)、Data(数据盘) - **Limit** (int) - 返回数据长度,默认为20,最大100。 - **Offset** (int) - 列表起始位置偏移量,默认为0。 @@ -1013,7 +1084,8 @@ def describe_disk(self, req=None, **kwargs): - **ChargeType** (str) - 硬盘计费模式。枚举值:Dynamic,表示小时;Month,表示月;Year,表示年; - **CreateTime** (int) - 创建时间。时间戳 - **DiskID** (str) - 硬盘ID - - **DiskStatus** (str) - 硬盘状态。Creating:创建中,BeingCloned:正在被克隆中,Unbound:已解绑,Unbounding:解绑中,Bounding:绑定中,Bound:已绑定,Upgrading:升级中,Deleting:删除中,Deleted:已删除,Releasing:销毁中,Released:已销毁 + - **DiskStatus** (str) - 硬盘状态。Creating:创建中,BeingCloned:正在被克隆中,Unbound:已解绑,Unbounding:解绑中,Bounding:绑定中,Bound:已绑定,Upgrading:升级中,Deleting:删除中,Deleted:已删除,Releasing:销毁中,Released:已销毁;Snapshoting(快照中);Rollbacking(回滚中) + - **DiskType** (str) - 硬盘用途类型,Boot(系统盘)、Data(数据盘) - **ExpireTime** (int) - 过期时间。时间戳 - **Name** (str) - 名称 - **Region** (str) - 地域 @@ -1036,7 +1108,9 @@ def describe_eip(self, req=None, **kwargs): - **Region** (str) - (Config) 地域。枚举值:cn,表示中国; - **Zone** (str) - (Required) 可用区。枚举值:zone-01,表示中国; + - **BindResourceID** (str) - 绑定资源ID,查询该资源绑定的所有 EIP - **EIPIDs** (list) - 【数组】外网的 ID。输入有效的 ID。调用方式举例:EIPIDs.0=“one-id”、EIPIDs.1=“two-id” + - **IPVersion** (str) - 版本,支持IPv4、IPv6 - **Limit** (str) - 返回数据长度,默认为20,最大100。 - **Offset** (str) - 列表起始位置偏移量,默认为0。 @@ -1053,11 +1127,14 @@ def describe_eip(self, req=None, **kwargs): - **Bandwidth** (int) - 带宽大小 - **BindResourceID** (str) - 绑定资源ID - **BindResourceType** (str) - 绑定资源类型 + - **CanDefaultGW** (int) - 所处线路是否为默认路由,1代表所处线路是默认路由;默认路由的可以设置成出口 - **ChargeType** (str) - 计费模式。枚举值:Dynamic,表示小时;Month,表示月;Year,表示年; - **CreateTime** (int) - 创建时间。时间戳 - **EIPID** (str) - ID - **ExpireTime** (int) - 过期时间。时间戳 - **IP** (str) - 外网IP + - **IPVersion** (str) - IP版本,支持值:IPv4\\IPv6 + - **ISDefaultGW** (int) - 是否为默认出口,1代表该IP地址为默认出口 - **Name** (str) - 名称 - **OperatorName** (str) - 线路 - **Region** (str) - 地域 @@ -1098,7 +1175,7 @@ def describe_image(self, req=None, **kwargs): - **CreateTime** (int) - 创建时间。时间戳。 - **ImageID** (str) - 镜像ID - - **ImageStatus** (str) - 镜像状态。枚举类型:Making(创建中),Available(可用),Unavailable(不可用),Terminating(销毁中),Used(被使用中),Deleting(删除中),Deleted(已删除), Uploading(导入中) + - **ImageStatus** (str) - 镜像状态。枚举类型:Making(创建中),Terminating(销毁中),Used(可用),Deleting(删除中),Deleted(已删除), Uploading(导入中), Failed(导入失败) - **ImageType** (str) - 镜像类型。枚举类型:Base(基础镜像),Custom(自制镜像)。 - **Name** (str) - 镜像名称 - **OSDistribution** (str) - 镜像系统发行版本。例如:Centos, Ubuntu, Windows等 @@ -1285,6 +1362,48 @@ def describe_natgw_rule(self, req=None, **kwargs): resp = self.invoke("DescribeNATGWRule", d, **kwargs) return apis.DescribeNATGWRuleResponseSchema().loads(resp) + def describe_nic(self, req=None, **kwargs): + """ DescribeNIC - 获取网卡信息 + + **Request** + + - **Region** (str) - (Config) 地域。枚举值: cn,表示中国; + - **Zone** (str) - (Required) 可用区。枚举值:zone-01,表示中国; + - **Limit** (int) - 返回数据长度,默认为20,最大100。 + - **NICIDs** (list) - 【数组】网卡的 ID。输入有效的 ID。调用方式举例:NICIDs.0=“one-id”、NICIDs.1=“two-id”。 + - **Offset** (int) - 列表起始位置偏移量,默认为0。 + + **Response** + + - **Infos** (list) - 见 **NICInfo** 模型定义 + - **Message** (str) - 返回信息描述。 + - **TotalCount** (int) - 返回网卡总个数。 + + **Response Model** + + **NICInfo** + + - **BindResourceID** (str) - 绑定资源ID + - **CreateTime** (int) - 创建时间。时间戳 + - **IP** (str) - IP + - **MAC** (str) - mac 地址 + - **NICID** (str) - 网卡ID + - **NICStatus** (str) - 网卡状态。枚举值。Creating:创建中,Free:未绑定,Unbounding:解绑中,Bounding:绑定中,Bound:已绑定,BindSGing:绑定安全组中,UnbindSGing:解绑安全组中,UpdateSGing:更新安全组中,Deleting:删除中,Deleted:已删除,Releasing:销毁中,Released:已销毁 + - **Name** (str) - 名称 + - **Region** (str) - 地域 + - **Remark** (str) - 备注 + - **SGID** (str) - 安全组ID + - **SubnetID** (str) - Subnet ID + - **VPCID** (str) - VPC ID + - **Zone** (str) - 可用区 + + """ + d = {"Region": self.config.region} + req and d.update(req) + d = apis.DescribeNICRequestSchema().dumps(d) + resp = self.invoke("DescribeNIC", d, **kwargs) + return apis.DescribeNICResponseSchema().loads(resp) + def describe_op_logs(self, req=None, **kwargs): """ DescribeOPLogs - 查询操作日志 @@ -1504,6 +1623,79 @@ def describe_security_group(self, req=None, **kwargs): resp = self.invoke("DescribeSecurityGroup", d, **kwargs) return apis.DescribeSecurityGroupResponseSchema().loads(resp) + def describe_security_group_resource(self, req=None, **kwargs): + """ DescribeSecurityGroupResource - 查询安全组绑定的资源信息 + + **Request** + + - **Region** (str) - (Config) 地域。枚举值: cn,表示中国; + - **SGID** (str) - (Required) 安全组ID + - **Zone** (str) - (Required) 可用区。枚举值:zone-01,表示中国; + - **Limit** (int) - 返回数据长度,默认为20,最大100。 + - **Offset** (int) - 列表起始位置偏移量,默认为0。 + + **Response** + + - **Infos** (list) - 见 **SGResourceInfo** 模型定义 + - **Message** (str) - 返回信息描述。 + - **TotalCount** (int) - 返回资源总个数。 + + **Response Model** + + **SGResourceInfo** + + - **Name** (str) - 资源名称 + - **Region** (str) - 地域 + - **ResourceID** (str) - 资源ID + - **ResourceType** (str) - 资源类型 + - **Zone** (str) - 可用区 + + """ + d = {"Region": self.config.region} + req and d.update(req) + d = apis.DescribeSecurityGroupResourceRequestSchema().dumps(d) + resp = self.invoke("DescribeSecurityGroupResource", d, **kwargs) + return apis.DescribeSecurityGroupResourceResponseSchema().loads(resp) + + def describe_snapshot(self, req=None, **kwargs): + """ DescribeSnapshot - 查询硬盘快照信息 + + **Request** + + - **Region** (str) - (Config) 地域。枚举值: cn,表示中国; + - **Zone** (str) - (Required) 可用区。枚举值:zone-01,表示中国; + - **DiskID** (str) - 硬盘ID,输入“有效”状态的ID + - **Limit** (int) - 返回数据长度,默认为20,最大100。 + - **Offset** (int) - 列表起始位置偏移量,默认为0。 + - **SnapshotIDs** (list) - 【数组】快照ID,输入“有效”的ID。调用方式举例:SnapshotIDs.0=“one-id”、SnapshotIDs.1=“two-id”。 + + **Response** + + - **Infos** (list) - 见 **SnapshotInfo** 模型定义 + - **Message** (str) - 返回信息描述 + - **TotalCount** (int) - 返回快照总个数 + + **Response Model** + + **SnapshotInfo** + + - **CreateTime** (int) - 快照创建时间 + - **DiskID** (str) - 快照对应的硬盘 ID + - **DiskType** (str) - 硬盘类型。枚举值:Boot,表示系统盘;Data,表示数据盘; + - **Name** (str) - 快照名称 + - **Region** (str) - 地域。枚举值: cn,表示中国; + - **Remark** (str) - 描述 + - **SnapshotID** (str) - 快照ID + - **SnapshotStatus** (str) - 快照状态。枚举值:Createing,表示制作中;Normal,表示正常;RollBacking,表示回滚中;Deleting,表示删除中;Deleted,表示已删除; + - **Zone** (str) - 可用区。枚举值:zone-01,表示中国; + + """ + d = {"Region": self.config.region} + req and d.update(req) + d = apis.DescribeSnapshotRequestSchema().dumps(d) + resp = self.invoke("DescribeSnapshot", d, **kwargs) + return apis.DescribeSnapshotResponseSchema().loads(resp) + def describe_storage_type(self, req=None, **kwargs): """ DescribeStorageType - 查询存储类型 @@ -1614,10 +1806,10 @@ def describe_vm_instance(self, req=None, **kwargs): **Request** - - **Region** (str) - (Config) 地域。枚举值: cn,表示中国; + - **Region** (str) - (Config) 地域或数据中心。枚举值: cn,表示中国; - **Zone** (str) - (Required) 可用区。枚举值:zone-01,表示中国; - **Limit** (int) - 返回数据长度,默认为20,最大100。 - - **Offset** (str) - 列表起始位置偏移量,默认为0。 + - **Offset** (int) - 列表起始位置偏移量,默认为0。 - **SubnetID** (str) - 子网 ID。输入“有效”状态的子网 ID。 - **VMIDs** (list) - 【数组】虚拟机的 ID。输入有效的 ID。调用方式举例:PrivateIp.0=“one-id”、PrivateIp.1=“two-id”。 - **VPCID** (str) - VPC ID。输入“有效”状态的VPC ID。 @@ -1630,21 +1822,6 @@ def describe_vm_instance(self, req=None, **kwargs): **Response Model** - **VMIPInfo** - - - **IP** (str) - IP 值 - - **IPVersion** (str) - IP版本,支持值:IPv4\\IPv6 - - **InterfaceID** (str) - 网卡 ID - - **IsElastic** (str) - 是否是弹性网卡。枚举值:Y,表示是;N,表示否; - - **MAC** (str) - MAC 地址值 - - **SGID** (str) - 安全组 ID - - **SGName** (str) - 安全组名称 - - **SubnetID** (str) - 子网 ID - - **SubnetName** (str) - 子网名称 - - **Type** (str) - IP 类型。枚举值:Private,表示内网;Public,表示外网;Physical,表示物理网; - - **VPCID** (str) - VPC ID - - **VPCName** (str) - VPC 名称 - **VMDiskInfo** - **DiskID** (str) - 磁盘 ID @@ -1654,6 +1831,21 @@ def describe_vm_instance(self, req=None, **kwargs): - **Size** (int) - 磁盘大小,单位 GB - **Type** (str) - 磁盘类型。枚举值:Boot,表示系统盘;Data,表示数据盘; + **VMIPInfo** + + - **IP** (str) - IP 值 + - **IPVersion** (str) - IP版本,支持值:IPv4\\IPv6 + - **InterfaceID** (str) - 网卡 ID,IP 地址绑定的网卡 ID + - **IsElastic** (str) - 是否是弹性网卡。枚举值:Y,表示是;N,表示否; + - **MAC** (str) - MAC 地址值 + - **SGID** (str) - 安全组 ID + - **SGName** (str) - 安全组名称 + - **SubnetID** (str) - 子网 ID,IP 为外网 IP 时为空; + - **SubnetName** (str) - 子网名称,IP 为外网 IP 时为空; + - **Type** (str) - IP 类型。枚举值:Private,表示内网;Public,表示外网。 + - **VPCID** (str) - VPC ID,IP 为外网 IP 时为空; + - **VPCName** (str) - VPC 名称,IP 为外网 IP 时为空; + **VMInstanceInfo** - **CPU** (int) - CPU 个数 @@ -1668,18 +1860,16 @@ def describe_vm_instance(self, req=None, **kwargs): - **OSName** (str) - 操作系统名称 - **OSType** (str) - 操作系统类型 - **Region** (str) - Region - - **RegionAlias** (str) - Region 别名 - **Remark** (str) - 备注 - **State** (str) - 虚拟机状态。枚举值:Initializing,表示初始化;Starting,表示启动中;Restarting,表示重启中;Running,表示运行;Stopping,表示关机中;Stopped,表示关机;Deleted,表示已删除;Resizing,表示修改配置中;Terminating,表示销毁中;Terminated,表示已销毁;Migrating,表示迁移中;WaitReinstall,表示等待重装系统;Reinstalling,表示重装中;Poweroffing,表示断电中;ChangeSGing,表示修改防火墙中; - **SubnetID** (str) - 子网 ID - **SubnetName** (str) - 子网 名称 - **VMID** (str) - 虚拟机 ID - - **VMType** (str) - 虚拟机类型 - - **VMTypeAlias** (str) - 虚拟机类型别名 + - **VMType** (str) - 虚拟机所在宿主机的集群类型 ID + - **VMTypeAlias** (str) - 虚拟机所在宿主机的集群类型名称 - **VPCID** (str) - VPC ID - **VPCName** (str) - VPC 名称 - **Zone** (str) - Zone - - **ZoneAlias** (str) - Zone 别名 """ d = {"Region": self.config.region} @@ -1821,6 +2011,7 @@ def describe_vs(self, req=None, **kwargs): **VSInfo** - **AlarmTemplateID** (str) - 告警模板ID + - **CACertificateID** (str) - CA证书ID,用于验证客户端证书的签名。仅当VServer监听协议为 HTTPS 且 SSLMode 为双向认证时有效。 - **CreateTime** (int) - 创建时间,时间戳 - **Domain** (str) - HTTP 健康检查时校验请求的 HOST 字段中的域名。当健康检查类型为端口检查时,该值为空。 - **HealthcheckType** (str) - 负载均衡的健康检查类型。枚举值:Port:端口检查;Path: HTTP检查 。 @@ -1833,6 +2024,9 @@ def describe_vs(self, req=None, **kwargs): - **Protocol** (str) - 协议 - **RSHealthStatus** (str) - 健康检查状态,枚举值,Empty:全部异常,Parts:部分异常,All:正常 - **RSInfos** (list) - 见 **RSInfo** 模型定义 + - **SSLMode** (str) - SSL认证模式,取值范围["simplex","duplex"]分别表示单向认证和双向认证。 + - **Scheduler** (str) - 负载均衡的调度算法。枚举值:wrr:加权轮训;least_conn:最小连接数;hash:原地址,四层lb使用。ip_hash:七层lb使用 + - **ServerCertificateID** (str) - 服务器证书ID,用于证明服务器的身份。仅当 VServer监听协议为 HTTPS 时有效。 - **UpdateTime** (int) - 更新时间,时间戳 - **VSID** (str) - VServer的ID - **VSPolicyInfos** (list) - 见 **VSPolicyInfo** 模型定义 @@ -1901,7 +2095,7 @@ def describe_vs_policy(self, req=None, **kwargs): return apis.DescribeVSPolicyResponseSchema().loads(resp) def detach_disk(self, req=None, **kwargs): - """ DetachDisk - 解绑UClouStack硬盘 + """ DetachDisk - 解绑硬盘 **Request** @@ -1921,6 +2115,27 @@ def detach_disk(self, req=None, **kwargs): resp = self.invoke("DetachDisk", d, **kwargs) return apis.DetachDiskResponseSchema().loads(resp) + def detach_nic(self, req=None, **kwargs): + """ DetachNIC - 解绑UClouStack网卡 + + **Request** + + - **Region** (str) - (Config) 地域。枚举值:cn,表示中国; + - **NICID** (str) - (Required) 网卡ID + - **ResourceID** (str) - (Required) 绑定的资源ID + - **Zone** (str) - (Required) 可用区。枚举值:zone-01,表示中国; + + **Response** + + - **Message** (str) - 返回信息描述。 + + """ + d = {"Region": self.config.region} + req and d.update(req) + d = apis.DetachNICRequestSchema().dumps(d) + resp = self.invoke("DetachNIC", d, **kwargs) + return apis.DetachNICResponseSchema().loads(resp) + def disable_rs(self, req=None, **kwargs): """ DisableRS - 禁用负载均衡的单个服务节点 @@ -2521,7 +2736,7 @@ def update_security_group_rule(self, req=None, **kwargs): **Request** - **Region** (str) - (Config) 地域。枚举值: cn,表示中国; - - **Rules** (list) - (Required) 【数组】规则。输入有效的 规则。调用方式举例:Rules.0=“TCP|23|0.0.0.0/0|ACCEPT|HIGH|1”、Rules.1=“TCP|55|0.0.0.0/0|ACCEPT|HIGH|1” + - **Rules** (list) - (Required) 【数组】规则。输入有效的 规则。调用方式举例:Rules.0=“TCP|23|0.0.0.0/0|ACCEPT|HIGH|1|sg_rule-wefvg34f”、Rules.1=“TCP|55|0.0.0.0/0|ACCEPT|HIGH|1|sg_rule-wefvggf” - **SGID** (str) - (Required) 安全组ID - **Zone** (str) - (Required) 可用区。枚举值:zone-01,表示中国; @@ -2594,7 +2809,7 @@ def update_vs_policy(self, req=None, **kwargs): return apis.UpdateVSPolicyResponseSchema().loads(resp) def upgrade_disk(self, req=None, **kwargs): - """ UpgradeDisk - 升级硬盘 + """ UpgradeDisk - 扩容硬盘,为保证数据完整性,容量扩容前建议暂停对当前硬盘的所有文件系统读写操作,并进入操作系统进行 `umount ` 或`脱机` 操作。 **Request** @@ -2605,7 +2820,9 @@ def upgrade_disk(self, req=None, **kwargs): **Response** + - **Action** (str) - 操作名称 - **Message** (str) - 返回信息描述。 + - **RetCode** (int) - 返回码 """ d = {"Region": self.config.region} diff --git a/ucloud/services/ucloudstack/schemas/apis.py b/ucloud/services/ucloudstack/schemas/apis.py index 1453d8c..e2460ff 100644 --- a/ucloud/services/ucloudstack/schemas/apis.py +++ b/ucloud/services/ucloudstack/schemas/apis.py @@ -20,6 +20,8 @@ class AllocateEIPRequestSchema(schema.RequestSchema): fields = { "Bandwidth": fields.Int(required=True, dump_to="Bandwidth"), "ChargeType": fields.Str(required=True, dump_to="ChargeType"), + "IP": fields.Str(required=False, dump_to="IP"), + "IPVersion": fields.Str(required=False, dump_to="IPVersion"), "Name": fields.Str(required=True, dump_to="Name"), "OperatorName": fields.Str(required=True, dump_to="OperatorName"), "Quantity": fields.Int(required=False, dump_to="Quantity"), @@ -65,6 +67,32 @@ class AttachDiskResponseSchema(schema.ResponseSchema): fields = {"Message": fields.Str(required=True, load_from="Message")} +""" +API: AttachNIC + +绑定UCloudStack网卡 +""" + + +class AttachNICRequestSchema(schema.RequestSchema): + """ AttachNIC - 绑定UCloudStack网卡 + """ + + fields = { + "NICID": fields.Str(required=True, dump_to="NICID"), + "Region": fields.Str(required=True, dump_to="Region"), + "ResourceID": fields.Str(required=True, dump_to="ResourceID"), + "Zone": fields.Str(required=True, dump_to="Zone"), + } + + +class AttachNICResponseSchema(schema.ResponseSchema): + """ AttachNIC - 绑定UCloudStack网卡 + """ + + fields = {"Message": fields.Str(required=True, load_from="Message")} + + """ API: BindAlarmTemplate @@ -95,12 +123,12 @@ class BindAlarmTemplateResponseSchema(schema.ResponseSchema): """ API: BindEIP -绑定UCoudStack外网IP +绑定外网 IP """ class BindEIPRequestSchema(schema.RequestSchema): - """ BindEIP - 绑定UCoudStack外网IP + """ BindEIP - 绑定外网 IP """ fields = { @@ -113,7 +141,7 @@ class BindEIPRequestSchema(schema.RequestSchema): class BindEIPResponseSchema(schema.ResponseSchema): - """ BindEIP - 绑定UCoudStack外网IP + """ BindEIP - 绑定外网 IP """ fields = {"Message": fields.Str(required=True, load_from="Message")} @@ -158,7 +186,7 @@ class BindSecurityGroupRequestSchema(schema.RequestSchema): """ fields = { - "NICID": fields.Str(required=False, dump_to="NICID"), + "NICType": fields.Str(required=False, dump_to="NICType"), "Region": fields.Str(required=True, dump_to="Region"), "ResourceID": fields.Str(required=True, dump_to="ResourceID"), "SGID": fields.Str(required=True, dump_to="SGID"), @@ -403,6 +431,38 @@ class CreateNATGWRuleResponseSchema(schema.ResponseSchema): } +""" +API: CreateNIC + +创建网卡 +""" + + +class CreateNICRequestSchema(schema.RequestSchema): + """ CreateNIC - 创建网卡 + """ + + fields = { + "IP": fields.Str(required=False, dump_to="IP"), + "Name": fields.Str(required=True, dump_to="Name"), + "Region": fields.Str(required=True, dump_to="Region"), + "SGID": fields.Str(required=False, dump_to="SGID"), + "SubnetID": fields.Str(required=True, dump_to="SubnetID"), + "VPCID": fields.Str(required=True, dump_to="VPCID"), + "Zone": fields.Str(required=True, dump_to="Zone"), + } + + +class CreateNICResponseSchema(schema.ResponseSchema): + """ CreateNIC - 创建网卡 + """ + + fields = { + "Message": fields.Str(required=True, load_from="Message"), + "NICID": fields.Str(required=True, load_from="NICID"), + } + + """ API: CreatePhysicalIP @@ -735,7 +795,9 @@ class CreateVSResponseSchema(schema.ResponseSchema): """ fields = { + "Action": fields.Str(required=True, load_from="Action"), "Message": fields.Str(required=False, load_from="Message"), + "RetCode": fields.Int(required=True, load_from="RetCode"), "VSID": fields.Str(required=False, load_from="VSID"), } @@ -923,6 +985,31 @@ class DeleteNATGWRuleResponseSchema(schema.ResponseSchema): fields = {"Message": fields.Str(required=True, load_from="Message")} +""" +API: DeleteNIC + +删除网卡 +""" + + +class DeleteNICRequestSchema(schema.RequestSchema): + """ DeleteNIC - 删除网卡 + """ + + fields = { + "NICID": fields.Str(required=True, dump_to="NICID"), + "Region": fields.Str(required=True, dump_to="Region"), + "Zone": fields.Str(required=True, dump_to="Zone"), + } + + +class DeleteNICResponseSchema(schema.ResponseSchema): + """ DeleteNIC - 删除网卡 + """ + + fields = {"Message": fields.Str(required=True, load_from="Message")} + + """ API: DeletePhysicalIP @@ -1228,6 +1315,7 @@ class DescribeDiskRequestSchema(schema.RequestSchema): fields = { "DiskIDs": fields.List(fields.Str()), + "DiskType": fields.Str(required=False, dump_to="DiskType"), "Limit": fields.Int(required=False, dump_to="Limit"), "Offset": fields.Int(required=False, dump_to="Offset"), "Region": fields.Str(required=True, dump_to="Region"), @@ -1260,7 +1348,9 @@ class DescribeEIPRequestSchema(schema.RequestSchema): """ fields = { + "BindResourceID": fields.Str(required=False, dump_to="BindResourceID"), "EIPIDs": fields.List(fields.Str()), + "IPVersion": fields.Str(required=False, dump_to="IPVersion"), "Limit": fields.Str(required=False, dump_to="Limit"), "Offset": fields.Str(required=False, dump_to="Offset"), "Region": fields.Str(required=True, dump_to="Region"), @@ -1456,6 +1546,39 @@ class DescribeNATGWRuleResponseSchema(schema.ResponseSchema): } +""" +API: DescribeNIC + +获取网卡信息 +""" + + +class DescribeNICRequestSchema(schema.RequestSchema): + """ DescribeNIC - 获取网卡信息 + """ + + fields = { + "Limit": fields.Int(required=False, dump_to="Limit"), + "NICIDs": fields.List(fields.Str()), + "Offset": fields.Int(required=False, dump_to="Offset"), + "Region": fields.Str(required=True, dump_to="Region"), + "Zone": fields.Str(required=True, dump_to="Zone"), + } + + +class DescribeNICResponseSchema(schema.ResponseSchema): + """ DescribeNIC - 获取网卡信息 + """ + + fields = { + "Infos": fields.List( + models.NICInfoSchema(), required=True, load_from="Infos" + ), + "Message": fields.Str(required=True, load_from="Message"), + "TotalCount": fields.Int(required=True, load_from="TotalCount"), + } + + """ API: DescribeOPLogs @@ -1628,6 +1751,73 @@ class DescribeSecurityGroupResponseSchema(schema.ResponseSchema): } +""" +API: DescribeSecurityGroupResource + +查询安全组绑定的资源信息 +""" + + +class DescribeSecurityGroupResourceRequestSchema(schema.RequestSchema): + """ DescribeSecurityGroupResource - 查询安全组绑定的资源信息 + """ + + fields = { + "Limit": fields.Int(required=False, dump_to="Limit"), + "Offset": fields.Int(required=False, dump_to="Offset"), + "Region": fields.Str(required=True, dump_to="Region"), + "SGID": fields.Str(required=True, dump_to="SGID"), + "Zone": fields.Str(required=True, dump_to="Zone"), + } + + +class DescribeSecurityGroupResourceResponseSchema(schema.ResponseSchema): + """ DescribeSecurityGroupResource - 查询安全组绑定的资源信息 + """ + + fields = { + "Infos": fields.List( + models.SGResourceInfoSchema(), required=True, load_from="Infos" + ), + "Message": fields.Str(required=True, load_from="Message"), + "TotalCount": fields.Int(required=True, load_from="TotalCount"), + } + + +""" +API: DescribeSnapshot + +查询硬盘快照信息 +""" + + +class DescribeSnapshotRequestSchema(schema.RequestSchema): + """ DescribeSnapshot - 查询硬盘快照信息 + """ + + fields = { + "DiskID": fields.Str(required=False, dump_to="DiskID"), + "Limit": fields.Int(required=False, dump_to="Limit"), + "Offset": fields.Int(required=False, dump_to="Offset"), + "Region": fields.Str(required=True, dump_to="Region"), + "SnapshotIDs": fields.List(fields.Str()), + "Zone": fields.Str(required=True, dump_to="Zone"), + } + + +class DescribeSnapshotResponseSchema(schema.ResponseSchema): + """ DescribeSnapshot - 查询硬盘快照信息 + """ + + fields = { + "Infos": fields.List( + models.SnapshotInfoSchema(), required=True, load_from="Infos" + ), + "Message": fields.Str(required=True, load_from="Message"), + "TotalCount": fields.Int(required=True, load_from="TotalCount"), + } + + """ API: DescribeStorageType @@ -1736,7 +1926,7 @@ class DescribeVMInstanceRequestSchema(schema.RequestSchema): fields = { "Limit": fields.Int(required=False, dump_to="Limit"), - "Offset": fields.Str(required=False, dump_to="Offset"), + "Offset": fields.Int(required=False, dump_to="Offset"), "Region": fields.Str(required=True, dump_to="Region"), "SubnetID": fields.Str(required=False, dump_to="SubnetID"), "VMIDs": fields.List(fields.Str()), @@ -1893,12 +2083,12 @@ class DescribeVSPolicyResponseSchema(schema.ResponseSchema): """ API: DetachDisk -解绑UClouStack硬盘 +解绑硬盘 """ class DetachDiskRequestSchema(schema.RequestSchema): - """ DetachDisk - 解绑UClouStack硬盘 + """ DetachDisk - 解绑硬盘 """ fields = { @@ -1910,7 +2100,33 @@ class DetachDiskRequestSchema(schema.RequestSchema): class DetachDiskResponseSchema(schema.ResponseSchema): - """ DetachDisk - 解绑UClouStack硬盘 + """ DetachDisk - 解绑硬盘 + """ + + fields = {"Message": fields.Str(required=True, load_from="Message")} + + +""" +API: DetachNIC + +解绑UClouStack网卡 +""" + + +class DetachNICRequestSchema(schema.RequestSchema): + """ DetachNIC - 解绑UClouStack网卡 + """ + + fields = { + "NICID": fields.Str(required=True, dump_to="NICID"), + "Region": fields.Str(required=True, dump_to="Region"), + "ResourceID": fields.Str(required=True, dump_to="ResourceID"), + "Zone": fields.Str(required=True, dump_to="Zone"), + } + + +class DetachNICResponseSchema(schema.ResponseSchema): + """ DetachNIC - 解绑UClouStack网卡 """ fields = {"Message": fields.Str(required=True, load_from="Message")} @@ -2741,12 +2957,12 @@ class UpdateVSPolicyResponseSchema(schema.ResponseSchema): """ API: UpgradeDisk -升级硬盘 +扩容硬盘,为保证数据完整性,容量扩容前建议暂停对当前硬盘的所有文件系统读写操作,并进入操作系统进行 `umount ` 或`脱机` 操作。 """ class UpgradeDiskRequestSchema(schema.RequestSchema): - """ UpgradeDisk - 升级硬盘 + """ UpgradeDisk - 扩容硬盘,为保证数据完整性,容量扩容前建议暂停对当前硬盘的所有文件系统读写操作,并进入操作系统进行 `umount ` 或`脱机` 操作。 """ fields = { @@ -2758,7 +2974,11 @@ class UpgradeDiskRequestSchema(schema.RequestSchema): class UpgradeDiskResponseSchema(schema.ResponseSchema): - """ UpgradeDisk - 升级硬盘 + """ UpgradeDisk - 扩容硬盘,为保证数据完整性,容量扩容前建议暂停对当前硬盘的所有文件系统读写操作,并进入操作系统进行 `umount ` 或`脱机` 操作。 """ - fields = {"Message": fields.Str(required=True, load_from="Message")} + fields = { + "Action": fields.Str(required=True, load_from="Action"), + "Message": fields.Str(required=True, load_from="Message"), + "RetCode": fields.Int(required=True, load_from="RetCode"), + } diff --git a/ucloud/services/ucloudstack/schemas/models.py b/ucloud/services/ucloudstack/schemas/models.py index fe8964f..0e3abaa 100644 --- a/ucloud/services/ucloudstack/schemas/models.py +++ b/ucloud/services/ucloudstack/schemas/models.py @@ -49,19 +49,20 @@ class DiskInfoSchema(schema.ResponseSchema): fields = { "AttachResourceID": fields.Str( - required=False, load_from="AttachResourceID" + required=True, load_from="AttachResourceID" ), - "ChargeType": fields.Str(required=False, load_from="ChargeType"), - "CreateTime": fields.Int(required=False, load_from="CreateTime"), - "DiskID": fields.Str(required=False, load_from="DiskID"), - "DiskStatus": fields.Str(required=False, load_from="DiskStatus"), - "ExpireTime": fields.Int(required=False, load_from="ExpireTime"), - "Name": fields.Str(required=False, load_from="Name"), - "Region": fields.Str(required=False, load_from="Region"), + "ChargeType": fields.Str(required=True, load_from="ChargeType"), + "CreateTime": fields.Int(required=True, load_from="CreateTime"), + "DiskID": fields.Str(required=True, load_from="DiskID"), + "DiskStatus": fields.Str(required=True, load_from="DiskStatus"), + "DiskType": fields.Str(required=True, load_from="DiskType"), + "ExpireTime": fields.Int(required=True, load_from="ExpireTime"), + "Name": fields.Str(required=True, load_from="Name"), + "Region": fields.Str(required=True, load_from="Region"), "Remark": fields.Str(required=False, load_from="Remark"), - "SetType": fields.Str(required=False, load_from="SetType"), - "Size": fields.Int(required=False, load_from="Size"), - "Zone": fields.Str(required=False, load_from="Zone"), + "SetType": fields.Str(required=True, load_from="SetType"), + "Size": fields.Int(required=True, load_from="Size"), + "Zone": fields.Str(required=True, load_from="Zone"), } @@ -77,11 +78,14 @@ class EIPInfoSchema(schema.ResponseSchema): "BindResourceType": fields.Str( required=False, load_from="BindResourceType" ), + "CanDefaultGW": fields.Int(required=False, load_from="CanDefaultGW"), "ChargeType": fields.Str(required=False, load_from="ChargeType"), "CreateTime": fields.Int(required=False, load_from="CreateTime"), "EIPID": fields.Str(required=False, load_from="EIPID"), "ExpireTime": fields.Int(required=False, load_from="ExpireTime"), "IP": fields.Str(required=False, load_from="IP"), + "IPVersion": fields.Str(required=False, load_from="IPVersion"), + "ISDefaultGW": fields.Int(required=False, load_from="ISDefaultGW"), "Name": fields.Str(required=False, load_from="Name"), "OperatorName": fields.Str(required=False, load_from="OperatorName"), "Region": fields.Str(required=False, load_from="Region"), @@ -200,6 +204,27 @@ class NATGWRuleInfoSchema(schema.ResponseSchema): } +class NICInfoSchema(schema.ResponseSchema): + """ NICInfo - 网卡信息 + """ + + fields = { + "BindResourceID": fields.Str(required=True, load_from="BindResourceID"), + "CreateTime": fields.Int(required=True, load_from="CreateTime"), + "IP": fields.Str(required=True, load_from="IP"), + "MAC": fields.Str(required=True, load_from="MAC"), + "NICID": fields.Str(required=True, load_from="NICID"), + "NICStatus": fields.Str(required=True, load_from="NICStatus"), + "Name": fields.Str(required=True, load_from="Name"), + "Region": fields.Str(required=True, load_from="Region"), + "Remark": fields.Str(required=True, load_from="Remark"), + "SGID": fields.Str(required=True, load_from="SGID"), + "SubnetID": fields.Str(required=True, load_from="SubnetID"), + "VPCID": fields.Str(required=True, load_from="VPCID"), + "Zone": fields.Str(required=True, load_from="Zone"), + } + + class OPLogInfoSchema(schema.ResponseSchema): """ OPLogInfo - 操作日志 """ @@ -320,6 +345,38 @@ class SGInfoSchema(schema.ResponseSchema): } +class SGResourceInfoSchema(schema.ResponseSchema): + """ SGResourceInfo - 安全组绑定的资源信息 + """ + + fields = { + "Name": fields.Str(required=True, load_from="Name"), + "Region": fields.Str(required=True, load_from="Region"), + "ResourceID": fields.Str(required=True, load_from="ResourceID"), + "ResourceType": fields.Str(required=True, load_from="ResourceType"), + "Zone": fields.Str(required=True, load_from="Zone"), + } + + +class SnapshotInfoSchema(schema.ResponseSchema): + """ SnapshotInfo - 快照的详细信息 + """ + + fields = { + "CreateTime": fields.Int(required=False, load_from="CreateTime"), + "DiskID": fields.Str(required=False, load_from="DiskID"), + "DiskType": fields.Str(required=False, load_from="DiskType"), + "Name": fields.Str(required=False, load_from="Name"), + "Region": fields.Str(required=False, load_from="Region"), + "Remark": fields.Str(required=False, load_from="Remark"), + "SnapshotID": fields.Str(required=False, load_from="SnapshotID"), + "SnapshotStatus": fields.Str( + required=False, load_from="SnapshotStatus" + ), + "Zone": fields.Str(required=False, load_from="Zone"), + } + + class StorageTypeInfoSchema(schema.ResponseSchema): """ StorageTypeInfo - 存储类型信息 """ @@ -369,7 +426,7 @@ class UserInfoSchema(schema.ResponseSchema): class VMDiskInfoSchema(schema.ResponseSchema): - """ VMDiskInfo - UCloudStack虚拟机磁盘信息 + """ VMDiskInfo - 虚拟机磁盘信息 """ fields = { @@ -383,7 +440,7 @@ class VMDiskInfoSchema(schema.ResponseSchema): class VMIPInfoSchema(schema.ResponseSchema): - """ VMIPInfo - UCloudStack虚拟机IP信息 + """ VMIPInfo - 虚拟机IP信息 """ fields = { @@ -419,7 +476,6 @@ class VMInstanceInfoSchema(schema.ResponseSchema): "OSName": fields.Str(required=False, load_from="OSName"), "OSType": fields.Str(required=False, load_from="OSType"), "Region": fields.Str(required=False, load_from="Region"), - "RegionAlias": fields.Str(required=False, load_from="RegionAlias"), "Remark": fields.Str(required=False, load_from="Remark"), "State": fields.Str(required=False, load_from="State"), "SubnetID": fields.Str(required=False, load_from="SubnetID"), @@ -430,7 +486,6 @@ class VMInstanceInfoSchema(schema.ResponseSchema): "VPCID": fields.Str(required=False, load_from="VPCID"), "VPCName": fields.Str(required=False, load_from="VPCName"), "Zone": fields.Str(required=False, load_from="Zone"), - "ZoneAlias": fields.Str(required=False, load_from="ZoneAlias"), } @@ -484,13 +539,16 @@ class VSPolicyInfoSchema(schema.ResponseSchema): class VSInfoSchema(schema.ResponseSchema): - """ VSInfo - RServer信息 + """ VSInfo - VServer信息 """ fields = { "AlarmTemplateID": fields.Str( required=True, load_from="AlarmTemplateID" ), + "CACertificateID": fields.Str( + required=False, load_from="CACertificateID" + ), "CreateTime": fields.Int(required=True, load_from="CreateTime"), "Domain": fields.Str(required=False, load_from="Domain"), "HealthcheckType": fields.Str( @@ -511,6 +569,11 @@ class VSInfoSchema(schema.ResponseSchema): "Protocol": fields.Str(required=True, load_from="Protocol"), "RSHealthStatus": fields.Str(required=True, load_from="RSHealthStatus"), "RSInfos": fields.List(RSInfoSchema()), + "SSLMode": fields.Str(required=False, load_from="SSLMode"), + "Scheduler": fields.Str(required=True, load_from="Scheduler"), + "ServerCertificateID": fields.Str( + required=False, load_from="ServerCertificateID" + ), "UpdateTime": fields.Int(required=True, load_from="UpdateTime"), "VSID": fields.Str(required=True, load_from="VSID"), "VSPolicyInfos": fields.List(VSPolicyInfoSchema()), diff --git a/ucloud/version.py b/ucloud/version.py index 3a06d93..3179213 100644 --- a/ucloud/version.py +++ b/ucloud/version.py @@ -1,3 +1,3 @@ # -*- coding: utf-8 -*- -version = "0.9.1" +version = "0.9.2"