├── docs ├── contents.rst ├── _static │ └── .gitkeep ├── helpers.rst ├── conf.py ├── core.rst ├── Makefile ├── index.rst ├── quick_start.rst ├── services.rst └── usage.rst ├── scripts ├── __init__.py └── migrate │ ├── __init__.py │ ├── requirements.txt │ ├── _plugin_doc.py │ ├── config.json │ ├── __main__.py │ ├── test_migrate.py │ ├── _plugin_py3to2.py │ └── _migrate.py ├── requirements.txt ├── tests ├── __init__.py ├── test_core │ ├── __init__.py │ ├── test_exc.py │ ├── test_encoder.py │ ├── test_auth.py │ ├── test_fields.py │ ├── test_transport.py │ └── test_client.py ├── test_unit │ ├── __init__.py │ ├── test_core │ │ ├── __init__.py │ │ ├── test_deco.py │ │ ├── test_exc.py │ │ ├── test_encoder.py │ │ ├── test_auth.py │ │ ├── test_fields.py │ │ ├── test_transport.py │ │ └── test_client.py │ ├── test_helpers │ │ ├── __init__.py │ │ ├── test_utils.py │ │ └── test_wait.py │ └── test_testing │ │ ├── __init__.py │ │ └── test_utest.py ├── test_helpers │ ├── __init__.py │ ├── test_utils.py │ └── test_wait.py ├── test_services │ ├── __init__.py │ ├── conftest.py │ ├── test_set_2935.py │ └── test_set_1202.py ├── test_testing │ ├── __init__.py │ └── test_utest.py └── test_acceptance │ └── conftest.py ├── ucloud ├── __init__.py ├── core │ ├── __init__.py │ ├── utils │ │ ├── __init__.py │ │ ├── compat.py │ │ ├── log.py │ │ ├── deco.py │ │ └── middleware.py │ ├── typesystem │ │ ├── __init__.py │ │ ├── encoder.py │ │ ├── abstract.py │ │ ├── schema.py │ │ └── fields.py │ ├── auth │ │ ├── __init__.py │ │ └── _cfg.py │ ├── client │ │ ├── __init__.py │ │ ├── _cfg.py │ │ └── _client.py │ ├── transport │ │ ├── __init__.py │ │ ├── utils.py │ │ ├── http.py │ │ └── _requests.py │ └── exc │ │ ├── __init__.py │ │ └── _exc.py ├── helpers │ ├── __init__.py │ ├── utils.py │ └── wait.py ├── services │ ├── __init__.py │ ├── pathx │ │ ├── __init__.py │ │ └── schemas │ │ │ ├── __init__.py │ │ │ ├── models.py │ │ │ └── apis.py │ ├── ucdn │ │ ├── __init__.py │ │ └── schemas │ │ │ └── __init__.py │ ├── udb │ │ ├── __init__.py │ │ └── schemas │ │ │ └── __init__.py │ ├── udisk │ │ ├── __init__.py │ │ └── schemas │ │ │ ├── __init__.py │ │ │ └── models.py │ ├── udpn │ │ ├── __init__.py │ │ └── schemas │ │ │ ├── __init__.py │ │ │ └── models.py │ ├── ufs │ │ ├── __init__.py │ │ └── schemas │ │ │ ├── __init__.py │ │ │ ├── models.py │ │ │ └── apis.py │ ├── uhost │ │ ├── __init__.py │ │ └── schemas │ │ │ └── __init__.py │ ├── uhub │ │ ├── __init__.py │ │ └── schemas │ │ │ ├── __init__.py │ │ │ ├── models.py │ │ │ └── apis.py │ ├── ulb │ │ ├── __init__.py │ │ └── schemas │ │ │ └── __init__.py │ ├── umem │ │ ├── __init__.py │ │ └── schemas │ │ │ └── __init__.py │ ├── unet │ │ ├── __init__.py │ │ └── schemas │ │ │ └── __init__.py │ ├── uphost │ │ ├── __init__.py │ │ └── schemas │ │ │ ├── __init__.py │ │ │ └── models.py │ ├── usms │ │ ├── __init__.py │ │ └── schemas │ │ │ ├── __init__.py │ │ │ └── models.py │ ├── vpc │ │ ├── __init__.py │ │ └── schemas │ │ │ ├── __init__.py │ │ │ └── models.py │ ├── ipsecvpn │ │ ├── __init__.py │ │ ├── schemas │ │ │ ├── __init__.py │ │ │ ├── apis.py │ │ │ └── models.py │ │ └── client.py │ ├── stepflow │ │ ├── __init__.py │ │ ├── schemas │ │ │ ├── __init__.py │ │ │ ├── models.py │ │ │ └── apis.py │ │ └── client.py │ ├── uaccount │ │ ├── __init__.py │ │ ├── schemas │ │ │ ├── __init__.py │ │ │ ├── models.py │ │ │ └── apis.py │ │ └── client.py │ └── ucloudstack │ │ ├── __init__.py │ │ └── schemas │ │ └── __init__.py ├── testing │ ├── __init__.py │ ├── driver │ │ ├── __init__.py │ │ ├── _specification.py │ │ └── _scenario.py │ ├── exc.py │ ├── env.py │ ├── mock.py │ ├── funcs.py │ └── op.py ├── version.py ├── _compat.py └── client.py ├── examples ├── two-tier │ ├── __init__.py │ ├── README.md │ └── main.py └── uhost │ ├── __init__.py │ ├── README.md │ └── main.py ├── pytest.ini ├── .editorconfig ├── .travis.yml ├── README.md ├── .gitignore ├── Makefile └── setup.py /docs/contents.rst: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/_static/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /scripts/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /scripts/migrate/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | requests 2 | -------------------------------------------------------------------------------- /scripts/migrate/requirements.txt: -------------------------------------------------------------------------------- 1 | astor 2 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /ucloud/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /ucloud/core/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /examples/two-tier/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /examples/uhost/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /tests/test_core/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /tests/test_unit/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /ucloud/core/utils/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /ucloud/helpers/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /ucloud/services/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /ucloud/testing/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /tests/test_helpers/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /tests/test_services/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /tests/test_testing/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /ucloud/core/typesystem/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /ucloud/services/pathx/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /ucloud/services/ucdn/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /ucloud/services/udb/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /ucloud/services/udisk/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /ucloud/services/udpn/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /ucloud/services/ufs/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /ucloud/services/uhost/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /ucloud/services/uhub/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /ucloud/services/ulb/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /ucloud/services/umem/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /ucloud/services/unet/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /ucloud/services/uphost/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /ucloud/services/usms/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /ucloud/services/vpc/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /tests/test_unit/test_core/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /tests/test_unit/test_helpers/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /tests/test_unit/test_testing/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /ucloud/services/ipsecvpn/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /ucloud/services/stepflow/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /ucloud/services/uaccount/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /ucloud/services/ucdn/schemas/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /ucloud/services/ucloudstack/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /ucloud/services/udb/schemas/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /ucloud/services/udpn/schemas/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /ucloud/services/ufs/schemas/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /ucloud/services/uhub/schemas/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /ucloud/services/ulb/schemas/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /ucloud/services/umem/schemas/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /ucloud/services/unet/schemas/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /ucloud/services/usms/schemas/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /ucloud/services/vpc/schemas/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /ucloud/services/ipsecvpn/schemas/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /ucloud/services/pathx/schemas/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /ucloud/services/stepflow/schemas/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /ucloud/services/uaccount/schemas/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /ucloud/services/udisk/schemas/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /ucloud/services/uhost/schemas/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /ucloud/services/uphost/schemas/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /ucloud/version.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | version = "0.9.2" 4 | -------------------------------------------------------------------------------- /ucloud/services/ucloudstack/schemas/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | -------------------------------------------------------------------------------- /ucloud/core/auth/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from ucloud.core.auth._cfg import Credential 4 | 5 | __all__ = ["Credential"] 6 | -------------------------------------------------------------------------------- /ucloud/testing/driver/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from ._specification import Specification 4 | from ._scenario import Scenario 5 | from ._step import Step 6 | 7 | spec = Specification() 8 | -------------------------------------------------------------------------------- /pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | log_cli = 1 3 | log_cli_level = INFO 4 | log_cli_format = %(asctime)s [%(levelname)8s] %(message)s (%(filename)s:%(lineno)s %(name)s) 5 | testpaths = ucloud tests 6 | addopts = --doctest-modules 7 | -------------------------------------------------------------------------------- /ucloud/core/client/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ 4 | Client 5 | """ 6 | from ucloud.core.client._cfg import Config 7 | from ucloud.core.client._client import Client 8 | 9 | __all__ = ["Config", "Client"] 10 | -------------------------------------------------------------------------------- /docs/helpers.rst: -------------------------------------------------------------------------------- 1 | Helpers 2 | ======= 3 | 4 | Wait Resource State 5 | ------------------- 6 | 7 | .. autofunction:: ucloud.helpers.wait.wait_for_state 8 | 9 | Utilities 10 | --------- 11 | 12 | .. automodule:: ucloud.helpers.utils 13 | :members: 14 | -------------------------------------------------------------------------------- /ucloud/core/transport/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from ucloud.core.transport.http import Request, Response, Transport, SSLOption 4 | from ucloud.core.transport._requests import RequestsTransport 5 | 6 | __all__ = ["Request", "Response", "Transport", "RequestsTransport", "SSLOption"] 7 | -------------------------------------------------------------------------------- /tests/test_unit/test_core/test_deco.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from ucloud.core.utils.deco import deprecated 4 | 5 | 6 | @deprecated(instead_of="bar") 7 | def foo(): 8 | pass 9 | 10 | 11 | def test_deprecated_deco(caplog): 12 | foo() 13 | assert "deprecated" in caplog.text 14 | -------------------------------------------------------------------------------- /scripts/migrate/_plugin_doc.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | class DocTransformer: 4 | def __init__(self, replaces=None): 5 | self.replaces = replaces 6 | 7 | def convert(self, source: str) -> str: 8 | result = source 9 | for r in self.replaces: 10 | result = result.replace(r['from'], r['to']) 11 | return result 12 | -------------------------------------------------------------------------------- /tests/test_unit/test_helpers/test_utils.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from ucloud.helpers import utils 4 | 5 | 6 | def test_b64encode(): 7 | encoded = utils.b64encode("foobar42") 8 | assert encoded == "Zm9vYmFyNDI=" 9 | 10 | 11 | def test_b64decode(): 12 | decoded = utils.b64decode("Zm9vYmFyNDI=") 13 | assert decoded == "foobar42" 14 | -------------------------------------------------------------------------------- /tests/test_helpers/test_utils.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from ucloud.helpers import utils 4 | 5 | 6 | def test_b64encode(): 7 | encoded = utils.b64encode("foobar42") 8 | assert encoded == "Zm9vYmFyNDI=" 9 | 10 | 11 | def test_b64decode(): 12 | decoded = utils.b64decode("Zm9vYmFyNDI=") 13 | assert decoded == "foobar42" 14 | -------------------------------------------------------------------------------- /ucloud/core/exc/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from ucloud.core.exc._exc import ( 4 | UCloudException, 5 | ValidationException, 6 | RetCodeException, 7 | RetryTimeoutException, 8 | ) 9 | 10 | __all__ = [ 11 | "UCloudException", 12 | "ValidationException", 13 | "RetCodeException", 14 | "RetryTimeoutException", 15 | ] 16 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | project = 'ucloud-sdk-python3' 4 | copyright = '2019, ucloud' 5 | author = 'ucloud' 6 | extensions = ['sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.todo', 7 | 'sphinx.ext.viewcode'] 8 | templates_path = ['_templates'] 9 | exclude_patterns = [] 10 | html_theme = 'alabaster' 11 | html_static_path = ['_static'] 12 | -------------------------------------------------------------------------------- /ucloud/core/utils/compat.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import sys 4 | 5 | PY3 = sys.version_info[0] == 3 6 | if PY3: 7 | str = str 8 | string_types = (str,) 9 | from collections.abc import Callable 10 | else: 11 | import types 12 | 13 | str = unicode # noqa: F821 # noqa: F821 14 | string_types = types.StringTypes 15 | from collections import Callable 16 | -------------------------------------------------------------------------------- /examples/uhost/README.md: -------------------------------------------------------------------------------- 1 | # UCloud SDK UHost Example 2 | 3 | ## What is the goal 4 | 5 | Create an uhost, wait it created, and remove all example data. 6 | 7 | ## Setup Environment 8 | 9 | ```go 10 | export UCLOUD_PUBLIC_KEY="your public key" 11 | export UCLOUD_PRIVATE_KEY="your private key" 12 | export UCLOUD_PROJECT_ID="your project id" 13 | ``` 14 | 15 | ## How to run 16 | 17 | ```sh 18 | python main.py 19 | ``` 20 | -------------------------------------------------------------------------------- /ucloud/testing/exc.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | class UTestError(Exception): 5 | pass 6 | 7 | 8 | class ValidateError(UTestError): 9 | def __init__(self, errors): 10 | self.errors = errors 11 | 12 | def __str__(self): 13 | return str(self.errors) 14 | 15 | 16 | class ValueNotFoundError(UTestError): 17 | pass 18 | 19 | 20 | class CompareError(UTestError): 21 | pass 22 | -------------------------------------------------------------------------------- /examples/two-tier/README.md: -------------------------------------------------------------------------------- 1 | # UCloud SDK Two-Tier Example 2 | 3 | ## What is the goal 4 | 5 | Build a two-tier architecture with ulb and uhost, and remove all example data. 6 | 7 | ## Setup Environment 8 | 9 | ```go 10 | export UCLOUD_PUBLIC_KEY="your public key" 11 | export UCLOUD_PRIVATE_KEY="your private key" 12 | export UCLOUD_PROJECT_ID="your project id" 13 | ``` 14 | 15 | ## How to run 16 | 17 | ```sh 18 | python main.py 19 | ``` 20 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | indent_style = space 7 | indent_size = 4 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | charset = utf-8 11 | end_of_line = lf 12 | 13 | [*.bat] 14 | indent_style = tab 15 | end_of_line = crlf 16 | 17 | [LICENSE] 18 | insert_final_newline = false 19 | 20 | [Makefile] 21 | indent_style = tab 22 | 23 | [*.{diff,patch}] 24 | trim_trailing_whitespace = false 25 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # Config file for automatic testing at travis-ci.org 2 | 3 | language: python 4 | python: 5 | - 2.7 6 | dist: xenial 7 | sudo: true 8 | 9 | # command to install dependencies, e.g. pip install -r requirements.txt --use-mirrors 10 | install: pip install -e .[ci] 11 | 12 | # command to run tests, e.g. python setup.py test 13 | script: 14 | - make lint 15 | - make test-cov 16 | 17 | after_success: 18 | - bash <(curl -s https://codecov.io/bash) 19 | -------------------------------------------------------------------------------- /tests/test_core/test_exc.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import pytest 4 | from ucloud.core import exc 5 | 6 | 7 | def test_ret_code_error(): 8 | assert not exc.UCloudException().retryable 9 | code_error = exc.RetCodeException("Foo", 1, "") 10 | assert str(code_error) 11 | assert not code_error.retryable 12 | assert code_error.json() == {"Action": "Foo", "Message": "", "RetCode": 1} 13 | validate_error = exc.ValidationException(ValueError("invalid type")) 14 | assert not validate_error.retryable 15 | assert str(validate_error) 16 | -------------------------------------------------------------------------------- /tests/test_unit/test_core/test_exc.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import pytest 4 | from ucloud.core import exc 5 | 6 | 7 | def test_ret_code_error(): 8 | assert not exc.UCloudException().retryable 9 | code_error = exc.RetCodeException("Foo", 1, "") 10 | assert str(code_error) 11 | assert not code_error.retryable 12 | assert code_error.json() == {"Action": "Foo", "Message": "", "RetCode": 1} 13 | validate_error = exc.ValidationException(ValueError("invalid type")) 14 | assert not validate_error.retryable 15 | assert str(validate_error) 16 | -------------------------------------------------------------------------------- /tests/test_unit/test_helpers/test_wait.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import pytest 4 | from ucloud.helpers import wait 5 | 6 | 7 | def test_wait(): 8 | with pytest.raises(wait.WaitTimeoutException): 9 | wait.wait_for_state( 10 | pending=["pending"], 11 | target=["running"], 12 | refresh=lambda: "pending", 13 | timeout=0.5, 14 | ) 15 | wait.wait_for_state( 16 | pending=["pending"], 17 | target=["running"], 18 | refresh=lambda: "running", 19 | timeout=0.5, 20 | ) 21 | -------------------------------------------------------------------------------- /tests/test_helpers/test_wait.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import pytest 4 | from ucloud.helpers import wait 5 | 6 | 7 | def test_wait(): 8 | with pytest.raises(wait.WaitTimeoutException): 9 | wait.wait_for_state( 10 | pending=["pending"], 11 | target=["running"], 12 | refresh=lambda: "pending", 13 | timeout=0.5, 14 | ) 15 | wait.wait_for_state( 16 | pending=["pending"], 17 | target=["running"], 18 | refresh=lambda: "running", 19 | timeout=0.5, 20 | ) 21 | -------------------------------------------------------------------------------- /scripts/migrate/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | { 4 | "name": "py3to2", 5 | "ext": [ 6 | ".py" 7 | ] 8 | }, 9 | { 10 | "name": "doc", 11 | "ext": [ 12 | ".md", 13 | ".rst" 14 | ], 15 | "options": { 16 | "replaces": [ 17 | { 18 | "from": "ucloud-sdk-python3", 19 | "to": "ucloud-sdk-python2" 20 | }, 21 | { 22 | "from": "UCloud SDK Python 3", 23 | "to": "UCloud SDK Python 2" 24 | } 25 | ] 26 | } 27 | } 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /docs/core.rst: -------------------------------------------------------------------------------- 1 | Core 2 | ==== 3 | 4 | Config 5 | ------ 6 | 7 | .. autoclass:: ucloud.core.auth.Credential 8 | :members: 9 | 10 | .. autoclass:: ucloud.core.client.Config 11 | :members: 12 | 13 | Client 14 | ------ 15 | 16 | .. autoclass:: ucloud.core.client.Client 17 | :members: 18 | 19 | Transport 20 | --------- 21 | 22 | .. autoclass:: ucloud.core.transport.RequestsTransport 23 | :members: 24 | 25 | Middleware 26 | ---------- 27 | 28 | .. autoclass:: ucloud.core.utils.middleware.Middleware 29 | :members: 30 | 31 | Testing 32 | ------- 33 | 34 | .. automodule:: ucloud.core.testing.env 35 | :members: 36 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | SOURCEDIR = . 8 | BUILDDIR = build 9 | 10 | # Put it first so that "make" without argument is like "make help". 11 | help: 12 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 13 | 14 | .PHONY: help Makefile 15 | 16 | # Catch-all target: route all unknown targets to Sphinx using the new 17 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 18 | %: Makefile 19 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 20 | -------------------------------------------------------------------------------- /ucloud/core/utils/log.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import logging 4 | 5 | DEFAULT_LOGGER_NAME = "ucloud" 6 | DEFAULT_FORMAT = "%(asctime)s [%(levelname)8s] %(message)s (%(filename)s:%(lineno)s %(name)s)" 7 | DEFAULT_LEVEL = logging.INFO 8 | 9 | 10 | def init_default_logger(): 11 | logger = logging.getLogger(DEFAULT_LOGGER_NAME) 12 | logger.setLevel(DEFAULT_LEVEL) 13 | ch = logging.StreamHandler() 14 | ch.setLevel(DEFAULT_LEVEL) 15 | formatter = logging.Formatter(DEFAULT_FORMAT) 16 | ch.setFormatter(formatter) 17 | logger.removeHandler(ch) 18 | logger.addHandler(ch) 19 | return logger 20 | 21 | 22 | default_logger = init_default_logger() 23 | -------------------------------------------------------------------------------- /ucloud/_compat.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from ucloud.core import client 4 | 5 | 6 | class CompactClient(client.Client): 7 | def __init__(self, config, transport=None, middleware=None): 8 | self._config = config 9 | super(CompactClient, self).__init__(config, transport, middleware) 10 | 11 | def pathx(self): 12 | from ucloud.services.pathx.client import PathXClient 13 | 14 | return PathXClient( 15 | self._config, self.transport, self.middleware, self.logger 16 | ) 17 | 18 | def vpc(self): 19 | from ucloud.services.vpc.client import VPCClient 20 | 21 | return VPCClient( 22 | self._config, self.transport, self.middleware, self.logger 23 | ) 24 | -------------------------------------------------------------------------------- /tests/test_unit/test_core/test_encoder.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import pytest 4 | from ucloud.core.typesystem import encoder 5 | 6 | 7 | @pytest.mark.parametrize( 8 | "input_vector,expected", 9 | [ 10 | ({"foo": "bar"}, {"foo": "bar"}), 11 | ({"foo": 42}, {"foo": "42"}), 12 | ({"foo": 42.42}, {"foo": "42.42"}), 13 | ({"foo": 42.0}, {"foo": "42"}), 14 | ({"foo": True}, {"foo": "true"}), 15 | ({"foo": False}, {"foo": "false"}), 16 | ({"IP": ["127.0.0.1"]}, {"IP.0": "127.0.0.1"}), 17 | ({"IP": ["foo", "bar"]}, {"IP.0": "foo", "IP.1": "bar"}), 18 | ({"IP": [{"foo": "bar"}]}, {"IP.0.foo": "bar"}), 19 | ], 20 | ) 21 | def test_params_encode(input_vector, expected): 22 | result = encoder.encode(input_vector) 23 | assert result == expected 24 | -------------------------------------------------------------------------------- /tests/test_services/conftest.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import os 4 | import pytest 5 | from ucloud.client import Client 6 | 7 | 8 | @pytest.fixture(scope="session", autouse=True, name="client") 9 | def client_factory(): 10 | return Client( 11 | { 12 | "region": "cn-bj2", 13 | "project_id": os.getenv("UCLOUD_PROJECT_ID"), 14 | "public_key": os.getenv("UCLOUD_PUBLIC_KEY"), 15 | "private_key": os.getenv("UCLOUD_PRIVATE_KEY"), 16 | "max_retries": 10, 17 | "timeout": 60, 18 | } 19 | ) 20 | 21 | 22 | @pytest.fixture(scope="module", autouse=True, name="variables") 23 | def variables_factory(): 24 | return { 25 | "Region": "cn-bj2", 26 | "Zone": "cn-bj2-05", 27 | "ProjectId": os.getenv("UCLOUD_PROJECT_ID"), 28 | } 29 | -------------------------------------------------------------------------------- /ucloud/testing/env.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import os 4 | 5 | ACC_ENV_KEY = "USDKACC" 6 | ACC_SKIP_REASON = "skip test for non-acc environment" 7 | 8 | 9 | def get_skip_reason(): 10 | return ACC_SKIP_REASON 11 | 12 | 13 | def is_acc(): 14 | """ check test env is acceptance testing or not """ 15 | return os.getenv(ACC_ENV_KEY) 16 | 17 | 18 | def is_ut(): 19 | """ check test env is unit testing or not """ 20 | return not is_acc() 21 | 22 | 23 | def pre_check_env(): 24 | """ pre check environment for testing 25 | 26 | NOTE: system environment variables credential is required for test environment 27 | """ 28 | assert os.getenv("UCLOUD_PUBLIC_KEY"), "invalid public key" 29 | assert os.getenv("UCLOUD_PRIVATE_KEY"), "invalid private key" 30 | assert os.getenv("UCLOUD_PROJECT_ID"), "invalid region" 31 | -------------------------------------------------------------------------------- /tests/test_core/test_encoder.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import pytest 4 | from ucloud.core.typesystem import encoder 5 | 6 | 7 | @pytest.mark.parametrize( 8 | "input_vector,expected", 9 | [ 10 | ({"foo": "bar"}, {"foo": "bar"}), 11 | ({"foo": 42}, {"foo": "42"}), 12 | ({"foo": 42.42}, {"foo": "42.42"}), 13 | ({"foo": 42.0}, {"foo": "42"}), 14 | ({"foo": True}, {"foo": "true"}), 15 | ({"foo": False}, {"foo": "false"}), 16 | ({"IP": ["127.0.0.1"]}, {"IP.0": "127.0.0.1"}), 17 | ({"TemplateParams": [u"中文"]}, {"TemplateParams.0": u"中文"}), 18 | ({"IP": ["foo", "bar"]}, {"IP.0": "foo", "IP.1": "bar"}), 19 | ({"IP": [{"foo": "bar"}]}, {"IP.0.foo": "bar"}), 20 | ], 21 | ) 22 | def test_params_encode(input_vector, expected): 23 | result = encoder.encode(input_vector) 24 | assert result == expected 25 | -------------------------------------------------------------------------------- /tests/test_unit/test_core/test_auth.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from ucloud.core import auth 4 | 5 | 6 | def test_verify_ac(): 7 | d = { 8 | "Action": "CreateUHostInstance", 9 | "CPU": 2, 10 | "ChargeType": "Month", 11 | "DiskSpace": 10, 12 | "ImageId": "f43736e1-65a5-4bea-ad2e-8a46e18883c2", 13 | "LoginMode": "Password", 14 | "Memory": 2048, 15 | "Name": "Host01", 16 | "Password": "VUNsb3VkLmNu", 17 | "PublicKey": "ucloudsomeone@example.com1296235120854146120", 18 | "Quantity": 1, 19 | "Region": "cn-bj2", 20 | "Zone": "cn-bj2-04", 21 | } 22 | cred = auth.Credential( 23 | "ucloudsomeone@example.com1296235120854146120", 24 | "46f09bb9fab4f12dfc160dae12273d5332b5debe", 25 | ) 26 | assert cred.verify_ac(d) == "4f9ef5df2abab2c6fccd1e9515cb7e2df8c6bb65" 27 | -------------------------------------------------------------------------------- /ucloud/testing/mock.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import json 4 | from ucloud.core.transport import Transport, Request, Response 5 | 6 | 7 | class MockedTransport(Transport): 8 | def __init__(self): 9 | super(MockedTransport, self).__init__() 10 | self.transport_handlers = [] 11 | self.client_handler = [] 12 | 13 | def send(self, req, **options): 14 | resp = Response(req.url, req.method) 15 | for handler in self.transport_handlers: 16 | resp = handler(req) 17 | for handler in self.client_handler: 18 | payload = handler(req.payload()) 19 | resp.content = json.dumps(payload).encode("utf-8") 20 | return resp 21 | 22 | def mock(self, handler): 23 | self.transport_handlers.append(handler) 24 | 25 | def mock_data(self, handler): 26 | self.client_handler.append(handler) 27 | -------------------------------------------------------------------------------- /tests/test_core/test_auth.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from ucloud.core import auth 4 | 5 | 6 | def test_verify_ac(): 7 | d = { 8 | "Action": "CreateUHostInstance", 9 | "CPU": 2, 10 | "ChargeType": "Month", 11 | "DiskSpace": 10, 12 | "ImageId": "f43736e1-65a5-4bea-ad2e-8a46e18883c2", 13 | "LoginMode": "Password", 14 | "Memory": 2048, 15 | "Name": "Host01", 16 | "Password": "VUNsb3VkLmNu", 17 | "PublicKey": "ucloudsomeone@example.com1296235120854146120", 18 | "Quantity": 1, 19 | "Region": "cn-bj2", 20 | "Zone": "cn-bj2-04", 21 | } 22 | cred = auth.Credential( 23 | "ucloudsomeone@example.com1296235120854146120", 24 | "46f09bb9fab4f12dfc160dae12273d5332b5debe", 25 | ) 26 | assert cred.verify_ac(d) == "4f9ef5df2abab2c6fccd1e9515cb7e2df8c6bb65" 27 | -------------------------------------------------------------------------------- /ucloud/core/utils/deco.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import functools 4 | import logging 5 | 6 | logger = logging.getLogger("ucloud") 7 | 8 | 9 | def deprecated(instead_of="", message=""): 10 | """ deprecated is a decorator to mark a function is deprecated. 11 | it will logging warning when this function called 12 | 13 | >>> @deprecated(instead_of="new_function") 14 | ... def an_old_function(): 15 | ... pass 16 | """ 17 | 18 | def deco(fn): 19 | @functools.wraps(fn) 20 | def wrapper(*args, **kwargs): 21 | msg = [ 22 | "this function/method `{}` is deprecated".format(fn.__name__) 23 | ] 24 | instead_of and msg.append( 25 | "please use `{}` instead".format(instead_of) 26 | ) 27 | message and msg.append(message) 28 | logger.warning(", ".join(msg)) 29 | return fn(*args, **kwargs) 30 | 31 | return wrapper 32 | 33 | return deco 34 | -------------------------------------------------------------------------------- /ucloud/core/typesystem/encoder.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from ucloud.core.utils.compat import str 4 | 5 | 6 | def encode(d): 7 | result = {} 8 | for k, v in d.items(): 9 | if isinstance(v, dict): 10 | for ek, ev in encode(v).items(): 11 | result["{}.{}".format(k, ek)] = encode_value(ev) 12 | elif isinstance(v, list): 13 | for i, item in enumerate(v): 14 | if isinstance(item, dict): 15 | for ek, ev in encode(item).items(): 16 | result["{}.{}.{}".format(k, i, ek)] = encode_value(ev) 17 | else: 18 | result["{}.{}".format(k, i)] = encode_value(item) 19 | else: 20 | result[k] = encode_value(v) 21 | return result 22 | 23 | 24 | def encode_value(v): 25 | if isinstance(v, bool): 26 | return "true" if v else "false" 27 | if isinstance(v, float): 28 | return str(int(v)) if v % 1 == 0 else str(v) 29 | return str(v) 30 | -------------------------------------------------------------------------------- /tests/test_unit/test_testing/test_utest.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import pytest 4 | from ucloud.testing import utest, env 5 | 6 | 7 | @pytest.mark.skipif(env.is_ut(), reason=env.get_skip_reason()) 8 | def test_acc_pre_check(self, client): 9 | assert str(client) 10 | assert client.config.to_dict() 11 | assert client.credential.to_dict() 12 | env.pre_check_env() 13 | 14 | 15 | def test_value_at_path(): 16 | d = { 17 | "Action": "DescribeEIPResponse", 18 | "EIPSet": [ 19 | { 20 | "Resource": { 21 | "ResourceID": "uhost-war3png3", 22 | "ResourceName": "eip-s1-bgp", 23 | "ResourceType": "uhost", 24 | "Zone": "cn-bj2-05", 25 | } 26 | } 27 | ], 28 | "RetCode": 0, 29 | "TotalBandwidth": 6, 30 | "TotalCount": 3, 31 | } 32 | assert ( 33 | utest.value_at_path(d, "EIPSet.0.Resource.ResourceID") 34 | == "uhost-war3png3" 35 | ) 36 | -------------------------------------------------------------------------------- /tests/test_testing/test_utest.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import pytest 4 | from ucloud.testing import utest, env 5 | 6 | 7 | @pytest.mark.skipif(env.is_ut(), reason=env.get_skip_reason()) 8 | def test_acc_pre_check(self, client): 9 | assert str(client) 10 | assert client.config.to_dict() 11 | assert client.credential.to_dict() 12 | env.pre_check_env() 13 | 14 | 15 | def test_value_at_path(): 16 | d = { 17 | "Action": "DescribeEIPResponse", 18 | "EIPSet": [ 19 | { 20 | "Resource": { 21 | "ResourceID": "uhost-war3png3", 22 | "ResourceName": "eip-s1-bgp", 23 | "ResourceType": "uhost", 24 | "Zone": "cn-bj2-05", 25 | } 26 | } 27 | ], 28 | "RetCode": 0, 29 | "TotalBandwidth": 6, 30 | "TotalCount": 3, 31 | } 32 | assert ( 33 | utest.value_at_path(d, "EIPSet.0.Resource.ResourceID") 34 | == "uhost-war3png3" 35 | ) 36 | -------------------------------------------------------------------------------- /ucloud/core/transport/utils.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import codecs 4 | 5 | _null = "\x00".encode("ascii") 6 | _null2 = _null * 2 7 | _null3 = _null * 3 8 | 9 | 10 | def guess_json_utf(data): 11 | """ guess_json_utf will detect the encoding of bytes, 12 | see `requests.utils.guess_json_utf` 13 | 14 | :rtype: str 15 | """ 16 | sample = data[:4] 17 | if sample in (codecs.BOM_UTF32_LE, codecs.BOM_UTF32_BE): 18 | return "utf-32" 19 | if sample[:3] == codecs.BOM_UTF8: 20 | return "utf-8-sig" 21 | if sample[:2] in (codecs.BOM_UTF16_LE, codecs.BOM_UTF16_BE): 22 | return "utf-16" 23 | nullcount = sample.count(_null) 24 | if nullcount == 0: 25 | return "utf-8" 26 | if nullcount == 2: 27 | if sample[::2] == _null2: 28 | return "utf-16-be" 29 | if sample[1::2] == _null2: 30 | return "utf-16-le" 31 | if nullcount == 3: 32 | if sample[:3] == _null3: 33 | return "utf-32-be" 34 | if sample[1:] == _null3: 35 | return "utf-32-le" 36 | return None 37 | -------------------------------------------------------------------------------- /scripts/migrate/__main__.py: -------------------------------------------------------------------------------- 1 | import os 2 | import json 3 | 4 | from scripts.migrate._migrate import Migrate, Config, PluginConfig 5 | 6 | 7 | DEFAULT_CONFIG_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'config.json') 8 | 9 | if __name__ == '__main__': 10 | import argparse 11 | 12 | parser = argparse.ArgumentParser(description='argument parser') 13 | parser.add_argument('--source', required=True, 14 | help='source code file writen by python3') 15 | parser.add_argument('--output', required=True, 16 | help='output source code file writen by python2') 17 | parser.add_argument('--config', required=False, 18 | help='the configuration file for migration processing') 19 | 20 | with open(DEFAULT_CONFIG_PATH) as f: 21 | d = json.load(f) 22 | 23 | config = Config() 24 | config.plugins = [PluginConfig(**plugin) for plugin in d['plugins']] 25 | print(config.__dict__) 26 | 27 | args = parser.parse_args() 28 | migrate = Migrate(config) 29 | migrate.run(args.source, args.output) 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | 5 |

UCloud SDK Python 2

6 | 7 |

8 | Latest Stable Version 9 | Travis CI Status 10 | Codecov Status 11 | Doc Status 12 |

13 | 14 | UCloud SDK is a Python client library for accessing the UCloud API. 15 | 16 | This client can run on Linux, macOS and Windows. 17 | 18 | - Website: https://www.ucloud.cn/ 19 | - Free software: Apache 2.0 license 20 | - [Documentation](https://ucloud.github.io/ucloud-sdk-python2/) 21 | -------------------------------------------------------------------------------- /ucloud/services/udpn/schemas/models.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ Code is generated by ucloud-model, DO NOT EDIT IT. """ 4 | from ucloud.core.typesystem import schema, fields 5 | 6 | 7 | class UDPNDataSchema(schema.ResponseSchema): 8 | """ UDPNData - UDPN 详细信息 9 | """ 10 | 11 | fields = { 12 | "Bandwidth": fields.Int(required=True, load_from="Bandwidth"), 13 | "ChargeType": fields.Str(required=True, load_from="ChargeType"), 14 | "CreateTime": fields.Int(required=True, load_from="CreateTime"), 15 | "ExpireTime": fields.Int(required=True, load_from="ExpireTime"), 16 | "Peer1": fields.Str(required=True, load_from="Peer1"), 17 | "Peer2": fields.Str(required=True, load_from="Peer2"), 18 | "UDPNId": fields.Str(required=True, load_from="UDPNId"), 19 | } 20 | 21 | 22 | class UDPNLineSetSchema(schema.ResponseSchema): 23 | """ UDPNLineSet - GetUDPNLineList 24 | """ 25 | 26 | fields = { 27 | "BandwidthUpperLimit": fields.Int( 28 | required=True, load_from="BandwidthUpperLimit" 29 | ), 30 | "LocalRegion": fields.Str(required=True, load_from="LocalRegion"), 31 | "RemoteRegion": fields.Str(required=True, load_from="RemoteRegion"), 32 | } 33 | -------------------------------------------------------------------------------- /ucloud/services/ufs/schemas/models.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ Code is generated by ucloud-model, DO NOT EDIT IT. """ 4 | from ucloud.core.typesystem import schema, fields 5 | 6 | 7 | class UFSVolumeInfo2Schema(schema.ResponseSchema): 8 | """ UFSVolumeInfo2 - 文件系统信息 9 | """ 10 | 11 | fields = { 12 | "CreateTime": fields.Int(required=False, load_from="CreateTime"), 13 | "ExpiredTime": fields.Int(required=False, load_from="ExpiredTime"), 14 | "IsExpired": fields.Str(required=False, load_from="IsExpired"), 15 | "MaxMountPointNum": fields.Int( 16 | required=True, load_from="MaxMountPointNum" 17 | ), 18 | "ProtocolType": fields.Str(required=True, load_from="ProtocolType"), 19 | "Remark": fields.Str(required=False, load_from="Remark"), 20 | "Size": fields.Int(required=False, load_from="Size"), 21 | "StorageType": fields.Str(required=True, load_from="StorageType"), 22 | "Tag": fields.Str(required=False, load_from="Tag"), 23 | "TotalMountPointNum": fields.Int( 24 | required=True, load_from="TotalMountPointNum" 25 | ), 26 | "UsedSize": fields.Int(required=False, load_from="UsedSize"), 27 | "VolumeId": fields.Str(required=True, load_from="VolumeId"), 28 | "VolumeName": fields.Str(required=True, load_from="VolumeName"), 29 | } 30 | -------------------------------------------------------------------------------- /scripts/migrate/test_migrate.py: -------------------------------------------------------------------------------- 1 | import ast 2 | import astor 3 | import pytest 4 | 5 | from scripts.migrate._plugin_py3to2 import SDK3to2Transformer 6 | 7 | 8 | @pytest.mark.parametrize( 9 | "input_vector,expected", 10 | [ 11 | ("foo: int = 42", "foo = 42"), 12 | ("def fn(foo: str) -> int: pass", "def fn(foo):\n pass"), 13 | ("class Foo: pass", "class Foo(object):\n pass"), 14 | ("import typing", ""), 15 | ("str(value)", "unicode(value)"), 16 | ("def deco(fn: typing.Callable[[Client, dict], dict]): pass", "def deco(fn):\n pass"), 17 | (""" 18 | def step(self, **kwargs): 19 | def deco(fn: typing.Callable[[Client, dict], dict]): 20 | return fn 21 | return deco 22 | """, """ 23 | def step(self, **kwargs): 24 | def deco(fn): 25 | return fn 26 | return deco 27 | """), 28 | ], 29 | ) 30 | def test_transformer(input_vector, expected): 31 | transformer = SDK3to2Transformer() 32 | result = transformer.convert(input_vector) 33 | assert result == astor.to_source(ast.parse(expected)) 34 | 35 | 36 | def test_parse_ast(): 37 | input_vector = """ 38 | def step(self, **kwargs): 39 | def deco(fn: typing.Callable[[Client, dict], dict]): 40 | return fn 41 | return deco 42 | """ 43 | node = ast.parse(input_vector) 44 | print(astor.dump_tree(node)) 45 | -------------------------------------------------------------------------------- /ucloud/services/pathx/schemas/models.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ Code is generated by ucloud-model, DO NOT EDIT IT. """ 4 | from ucloud.core.typesystem import schema, fields 5 | 6 | 7 | class GlobalSSHAreaSchema(schema.ResponseSchema): 8 | """ GlobalSSHArea - GlobalSSH覆盖地区,包括关联的UCloud机房信息 9 | """ 10 | 11 | fields = { 12 | "Area": fields.Str(required=True, load_from="Area"), 13 | "AreaCode": fields.Str(required=True, load_from="AreaCode"), 14 | "RegionSet": fields.List(fields.Str()), 15 | } 16 | 17 | 18 | class GlobalSSHInfoSchema(schema.ResponseSchema): 19 | """ GlobalSSHInfo - GlobalSSH实例信息 20 | """ 21 | 22 | fields = { 23 | "AcceleratingDomain": fields.Str( 24 | required=True, load_from="AcceleratingDomain" 25 | ), 26 | "Area": fields.Str(required=True, load_from="Area"), 27 | "ChargeType": fields.Str(required=True, load_from="ChargeType"), 28 | "CreateTime": fields.Int(required=True, load_from="CreateTime"), 29 | "ExpireTime": fields.Int(required=True, load_from="ExpireTime"), 30 | "InstanceId": fields.Str(required=True, load_from="InstanceId"), 31 | "Port": fields.Int(required=True, load_from="Port"), 32 | "Remark": fields.Str(required=True, load_from="Remark"), 33 | "TargetIP": fields.Str(required=True, load_from="TargetIP"), 34 | } 35 | -------------------------------------------------------------------------------- /ucloud/services/stepflow/schemas/models.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ Code is generated by ucloud-model, DO NOT EDIT IT. """ 4 | from ucloud.core.typesystem import schema, fields 5 | 6 | 7 | class ParamSchema(schema.ResponseSchema): 8 | """ Param - 工作流参数 9 | """ 10 | 11 | fields = { 12 | "Name": fields.Str(required=False, load_from="Name"), 13 | "Type": fields.Str(required=False, load_from="Type"), 14 | "Value": fields.Str(required=False, load_from="Value"), 15 | } 16 | 17 | 18 | class ActivityTemplateSchema(schema.ResponseSchema): 19 | """ ActivityTemplate - 工作流的Activity定义 20 | """ 21 | 22 | fields = { 23 | "Input": fields.Str(), 24 | "Name": fields.Str(required=False, load_from="Name"), 25 | "Next": fields.Str(required=False, load_from="Next"), 26 | "Output": fields.List(fields.Str()), 27 | "RetryTimes": fields.Str(required=False, load_from="RetryTimes"), 28 | "Timeout": fields.Str(required=False, load_from="Timeout"), 29 | "Type": fields.Str(required=False, load_from="Type"), 30 | } 31 | 32 | 33 | class WorkflowTemplateSchema(schema.ResponseSchema): 34 | """ WorkflowTemplate - Workflow对象定义 35 | """ 36 | 37 | fields = { 38 | "Activites": fields.List(ActivityTemplateSchema()), 39 | "Input": fields.List(ParamSchema()), 40 | "Output": fields.List(ParamSchema()), 41 | } 42 | -------------------------------------------------------------------------------- /ucloud/core/exc/_exc.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import collections 4 | from ucloud.core.utils import compat 5 | 6 | 7 | class UCloudException(Exception): 8 | @property 9 | def retryable(self): 10 | return False 11 | 12 | 13 | MAX_COMMON_RET_CODE = 2000 14 | 15 | 16 | class RetCodeException(UCloudException): 17 | def __init__(self, action, code, message): 18 | self.action = action 19 | self.code = code 20 | self.message = message 21 | 22 | @property 23 | def retryable(self): 24 | return self.code > MAX_COMMON_RET_CODE 25 | 26 | def __str__(self): 27 | return "{self.action} - {self.code}: {self.message}".format(self=self) 28 | 29 | def json(self): 30 | return { 31 | "RetCode": self.code, 32 | "Message": self.message or "", 33 | "Action": self.action or "", 34 | } 35 | 36 | 37 | class RetryTimeoutException(UCloudException): 38 | pass 39 | 40 | 41 | class ValidationException(UCloudException): 42 | def __init__(self, e=None): 43 | if isinstance(e, compat.string_types): 44 | self.errors = [e] 45 | elif isinstance(e, collections.Iterable): 46 | self.errors = e or [] 47 | else: 48 | self.errors = [e] 49 | 50 | @property 51 | def retryable(self): 52 | return False 53 | 54 | def __str__(self): 55 | return str([str(e) for e in self.errors]) 56 | -------------------------------------------------------------------------------- /tests/test_acceptance/conftest.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import json 4 | import os 5 | import pytest 6 | from ucloud.testing.driver import spec 7 | from ucloud.client import Client 8 | 9 | 10 | @pytest.fixture(scope="session", autouse=True) 11 | def ustack_client(): 12 | return Client( 13 | { 14 | "base_url": "http://console.pre.ucloudstack.com/api", 15 | "region": "cn", 16 | "public_key": os.getenv("UCLOUDSTACK_PUBLIC_KEY"), 17 | "private_key": os.getenv("UCLOUDSTACK_PRIVATE_KEY"), 18 | "max_retries": 10, 19 | "timeout": 60, 20 | } 21 | ) 22 | 23 | 24 | @pytest.fixture(scope="session", autouse=True) 25 | def client(): 26 | return Client( 27 | { 28 | "region": "cn-bj2", 29 | "project_id": os.getenv("UCLOUD_PROJECT_ID"), 30 | "public_key": os.getenv("UCLOUD_PUBLIC_KEY"), 31 | "private_key": os.getenv("UCLOUD_PRIVATE_KEY"), 32 | "max_retries": 10, 33 | "timeout": 60, 34 | } 35 | ) 36 | 37 | 38 | @pytest.fixture(scope="module", autouse=True, name="variables") 39 | def variables_factory(): 40 | return { 41 | "Region": "cn-bj2", 42 | "Zone": "cn-bj2-05", 43 | "ProjectId": os.getenv("UCLOUD_PROJECT_ID"), 44 | } 45 | 46 | 47 | @pytest.fixture(scope="session", autouse=True) 48 | def save_report(request): 49 | def save_report_handler(): 50 | with open("./report.json", "w") as f: 51 | json.dump(spec.json(), f) 52 | 53 | request.addfinalizer(save_report_handler) 54 | -------------------------------------------------------------------------------- /ucloud/services/uhub/schemas/models.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ Code is generated by ucloud-model, DO NOT EDIT IT. """ 4 | from ucloud.core.typesystem import schema, fields 5 | 6 | 7 | class TagSetSchema(schema.ResponseSchema): 8 | """ TagSet - Tag详细信息 9 | """ 10 | 11 | fields = { 12 | "TagName": fields.Str(required=True, load_from="TagName"), 13 | "UpdateTime": fields.Str(required=True, load_from="UpdateTime"), 14 | } 15 | 16 | 17 | class RepoSetSchema(schema.ResponseSchema): 18 | """ RepoSet - 镜像仓库 19 | """ 20 | 21 | fields = { 22 | "CreateTime": fields.Str(required=True, load_from="CreateTime"), 23 | "Description": fields.Str(required=True, load_from="Description"), 24 | "IsOutSide": fields.Str(required=True, load_from="IsOutSide"), 25 | "IsShared": fields.Str(required=True, load_from="IsShared"), 26 | "RepoName": fields.Str(required=True, load_from="RepoName"), 27 | "UpdateTime": fields.Str(required=True, load_from="UpdateTime"), 28 | } 29 | 30 | 31 | class ImageSetSchema(schema.ResponseSchema): 32 | """ ImageSet - 镜像信息 33 | """ 34 | 35 | fields = { 36 | "CreateTime": fields.Str(required=True, load_from="CreateTime"), 37 | "ImageName": fields.Str(required=True, load_from="ImageName"), 38 | "LatestTag": fields.Str(required=True, load_from="LatestTag"), 39 | "PullCount": fields.Int(required=True, load_from="PullCount"), 40 | "RepoName": fields.Str(required=True, load_from="RepoName"), 41 | "UpdateTime": fields.Str(required=True, load_from="UpdateTime"), 42 | } 43 | -------------------------------------------------------------------------------- /ucloud/core/typesystem/abstract.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from ucloud.core import exc 4 | 5 | 6 | class Field(object): 7 | def __init__( 8 | self, 9 | required=False, 10 | default=None, 11 | dump_to=None, 12 | load_from=None, 13 | strict=None, 14 | **kwargs 15 | ): 16 | self.required = required 17 | self.default = default 18 | self.dump_to = dump_to 19 | self.load_from = load_from 20 | self.options = kwargs 21 | self.strict = bool(strict) 22 | 23 | def dumps(self, value, **kwargs): 24 | raise NotImplementedError 25 | 26 | def loads(self, value, **kwargs): 27 | raise NotImplementedError 28 | 29 | @staticmethod 30 | def fail(name, expected, got): 31 | msg = "invalid field {}, expect {}, got {}".format(name, expected, got) 32 | raise exc.ValidationException(msg) 33 | 34 | 35 | class Schema(object): 36 | fields = {} 37 | 38 | def __init__( 39 | self, 40 | required=False, 41 | default=dict, 42 | dump_to=None, 43 | load_from=None, 44 | strict=False, 45 | case_sensitive=False, 46 | **kwargs 47 | ): 48 | self.required = required 49 | self.default = default 50 | self.dump_to = dump_to 51 | self.load_from = load_from 52 | self.options = kwargs 53 | self.strict = strict 54 | self.case_sensitive = case_sensitive 55 | 56 | def dumps(self, d): 57 | raise NotImplementedError 58 | 59 | def loads(self, d): 60 | raise NotImplementedError 61 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | UCloud SDK Python 2 2 | =================== 3 | 4 | UCloud SDK is a Python client library for accessing the UCloud API. 5 | 6 | This client can run on Linux, macOS and Windows. 7 | 8 | - Website: https://www.ucloud.cn/ 9 | - Free software: Apache 2.0 license 10 | - `Documentation `_ 11 | 12 | .. image:: https://img.shields.io/pypi/v/ucloud-sdk-python2.svg 13 | :target: https://pypi.python.org/pypi/ucloud-sdk-python2/ 14 | :alt: Latest Version 15 | .. image:: https://travis-ci.org/ucloud/ucloud-sdk-python2.svg?branch=master 16 | :target: https://travis-ci.org/ucloud/ucloud-sdk-python2 17 | :alt: Travis CI Status 18 | .. image:: https://codecov.io/github/ucloud/ucloud-sdk-python2/coverage.svg?branch=master 19 | :target: https://codecov.io/github/ucloud/ucloud-sdk-python2?branch=master 20 | :alt: Codecov Status 21 | .. image:: https://img.shields.io/badge/docs-passing-brightgreen.svg 22 | :target: https://ucloud.github.io/ucloud-sdk-python2/ 23 | :alt: Doc Status 24 | 25 | Guide 26 | ----- 27 | 28 | .. toctree:: 29 | :maxdepth: 2 30 | 31 | quick_start 32 | usage 33 | 34 | .. include:: quick_start.rst 35 | 36 | API Reference 37 | ------------- 38 | 39 | .. toctree:: 40 | :maxdepth: 2 41 | 42 | services 43 | helpers 44 | core 45 | 46 | Indices and tables 47 | ------------------ 48 | 49 | * :ref:`genindex` 50 | * :ref:`search` 51 | 52 | Feedback & Contribution 53 | ----------------------- 54 | 55 | - `Issue `_ 56 | - `Pull Request `_ 57 | -------------------------------------------------------------------------------- /docs/quick_start.rst: -------------------------------------------------------------------------------- 1 | QuickStart 2 | ========== 3 | 4 | Installation 5 | ------------ 6 | 7 | Install with pip: 8 | 9 | .. code:: shell 10 | 11 | pip install ucloud-sdk-python2 12 | 13 | Install with source code: 14 | 15 | .. code:: shell 16 | 17 | clone https://github.com/ucloud/ucloud-sdk-python2.git 18 | cd ucloud-sdk-python2 19 | python setup.py install 20 | 21 | QuickStart 22 | ---------- 23 | 24 | Currently, user public key & private key is the only method of authenticating with the API. You could get your keys here: 25 | 26 | - `Key Generation `_ 27 | 28 | You can then use your keys to create a new client of uhost service: 29 | 30 | .. code-block:: python 31 | 32 | from ucloud.core import exc 33 | from ucloud.client import Client 34 | 35 | client = Client({ 36 | "region": "cn-bj2", 37 | "project_id": "...", 38 | "public_key": "...", 39 | "private_key": "...", 40 | }) 41 | 42 | try: 43 | resp = client.uhost().create_uhost_instance({ 44 | 'Name': 'sdk-python-quickstart', 45 | 'Zone': image["zone"], 46 | 'ImageId': image["image_id"], 47 | 'LoginMode': "Password", 48 | 'Password': utils.b64encode(utils.gen_password(20)), 49 | 'CPU': 1, 50 | 'Memory': 1, 51 | 'Disks': [{ 52 | 'Size': 10, 53 | 'Type': 'CLOUD_SSD' 54 | }], 55 | }) 56 | except exc.UCloudException as e: 57 | print(e) 58 | else: 59 | print(resp) 60 | 61 | .. note:: UHost created above cannot be accessed via Internet unless an EIP is created and bind to the UHost. 62 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | db.sqlite3 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ 105 | 106 | # IDE / Editor 107 | .idea/ 108 | .vscode/ 109 | ~* 110 | 111 | # UCloud 112 | .migrate/ 113 | -------------------------------------------------------------------------------- /tests/test_core/test_fields.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import logging 4 | import pytest 5 | from ucloud.core import exc 6 | from ucloud.core.typesystem import fields 7 | 8 | logger = logging.getLogger(__name__) 9 | 10 | 11 | def test_field_str(): 12 | s = fields.Str() 13 | assert s.dumps(s.loads("foo")) == "foo" 14 | with pytest.raises(exc.ValidationException): 15 | fields.Str(strict=True).loads(42) 16 | 17 | 18 | def test_field_int(): 19 | i = fields.Int() 20 | assert i.dumps(i.loads("42")) == 42 21 | with pytest.raises(exc.ValidationException): 22 | fields.Int().dumps("foo") 23 | with pytest.raises(exc.ValidationException): 24 | fields.Int(strict=True).loads("42") 25 | 26 | 27 | def test_field_float(): 28 | f = fields.Float() 29 | assert f.dumps(f.loads("42.0")) == 42.0 30 | with pytest.raises(exc.ValidationException): 31 | fields.Float().dumps("foo") 32 | with pytest.raises(exc.ValidationException): 33 | fields.Float(strict=True).loads("42.0") 34 | 35 | 36 | def test_field_bool(): 37 | b = fields.Bool() 38 | assert b.dumps(b.loads("true")) 39 | assert not b.dumps(b.loads("false")) 40 | with pytest.raises(exc.ValidationException): 41 | fields.Bool().dumps("foo") 42 | with pytest.raises(exc.ValidationException): 43 | fields.Bool(strict=True).loads("true") 44 | 45 | 46 | def test_field_list(): 47 | l = fields.List(fields.Int()) 48 | assert l.dumps(l.loads(["42"])) == [42] 49 | with pytest.raises(exc.ValidationException): 50 | fields.List(fields.Int(), strict=True).dumps(42) 51 | with pytest.raises(exc.ValidationException): 52 | fields.List(fields.Int()).dumps(["foo"]) 53 | with pytest.raises(exc.ValidationException): 54 | fields.List(fields.Int(strict=True)).loads(["42"]) 55 | 56 | 57 | def test_field_base64(): 58 | b64 = fields.Base64() 59 | assert b64.loads("Zm9v") == "foo" 60 | assert b64.dumps("foo") == "Zm9v" 61 | -------------------------------------------------------------------------------- /scripts/migrate/_plugin_py3to2.py: -------------------------------------------------------------------------------- 1 | import ast 2 | 3 | import astor 4 | 5 | 6 | class SDK3to2Transformer(ast.NodeTransformer): 7 | UTF8_HEADER = '# -*- coding: utf-8 -*-\n\n' 8 | 9 | def visit_Import(self, node): 10 | is_typing = [alias for alias in node.names if alias.name == 'typing'] 11 | return None if is_typing else node 12 | 13 | def visit_AnnAssign(self, node): 14 | if node.value is None: 15 | return node 16 | return ast.Assign(targets=[node.target], value=node.value) 17 | 18 | def visit_FunctionDef(self, node): 19 | body = [] 20 | 21 | # remove type hints 22 | for arg in node.args.args: 23 | arg.annotation = None 24 | 25 | if node.args.kwarg: 26 | node.args.kwarg.annotation = None 27 | 28 | # visit children 29 | for child in node.body: 30 | if isinstance(child, ast.FunctionDef): 31 | body.append(self.visit_FunctionDef(child)) 32 | elif isinstance(child, ast.AnnAssign): 33 | body.append(self.visit_AnnAssign(child)) 34 | else: 35 | body.append(child) 36 | 37 | node.body = body 38 | node.returns = None 39 | return node 40 | 41 | def visit_ClassDef(self, node): 42 | body = [] 43 | 44 | # old-style class convert to new-style class 45 | if not node.bases: 46 | node.bases = [ast.Name(id='object', ctx=ast.Load())] 47 | 48 | # visit children 49 | for child in node.body: 50 | if isinstance(child, ast.FunctionDef): 51 | body.append(self.visit_FunctionDef(child)) 52 | else: 53 | body.append(child) 54 | 55 | node.body = body 56 | return node 57 | 58 | def convert(self, source: str) -> str: 59 | ast_node = ast.parse(source) 60 | ast_node = self.visit(ast_node) 61 | return self.UTF8_HEADER + astor.to_source(ast_node) 62 | -------------------------------------------------------------------------------- /tests/test_unit/test_core/test_fields.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import logging 4 | import pytest 5 | from ucloud.core import exc 6 | from ucloud.core.typesystem import fields 7 | 8 | logger = logging.getLogger(__name__) 9 | 10 | 11 | def test_field_str(): 12 | s = fields.Str() 13 | assert s.dumps(s.loads("foo")) == "foo" 14 | with pytest.raises(exc.ValidationException): 15 | fields.Str(strict=True).loads(42) 16 | 17 | 18 | def test_field_int(): 19 | i = fields.Int() 20 | assert i.dumps(i.loads("42")) == 42 21 | with pytest.raises(exc.ValidationException): 22 | fields.Int().dumps("foo") 23 | with pytest.raises(exc.ValidationException): 24 | fields.Int(strict=True).loads("42") 25 | 26 | 27 | def test_field_float(): 28 | f = fields.Float() 29 | assert f.dumps(f.loads("42.0")) == 42.0 30 | with pytest.raises(exc.ValidationException): 31 | fields.Float().dumps("foo") 32 | with pytest.raises(exc.ValidationException): 33 | fields.Float(strict=True).loads("42.0") 34 | 35 | 36 | def test_field_bool(): 37 | b = fields.Bool() 38 | assert b.dumps(b.loads("true")) 39 | assert not b.dumps(b.loads("false")) 40 | with pytest.raises(exc.ValidationException): 41 | fields.Bool().dumps("foo") 42 | with pytest.raises(exc.ValidationException): 43 | fields.Bool(strict=True).loads("true") 44 | 45 | 46 | def test_field_list(): 47 | l = fields.List(fields.Int()) 48 | assert l.dumps(l.loads(["42"])) == [42] 49 | with pytest.raises(exc.ValidationException): 50 | fields.List(fields.Int(), strict=True).dumps(42) 51 | with pytest.raises(exc.ValidationException): 52 | fields.List(fields.Int()).dumps(["foo"]) 53 | with pytest.raises(exc.ValidationException): 54 | fields.List(fields.Int(strict=True)).loads(["42"]) 55 | 56 | 57 | def test_field_base64(): 58 | b64 = fields.Base64() 59 | assert b64.loads("Zm9v") == "foo" 60 | assert b64.dumps("foo") == "Zm9v" 61 | -------------------------------------------------------------------------------- /ucloud/testing/driver/_specification.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from ucloud.testing.driver import _scenario 4 | 5 | 6 | class Specification(object): 7 | def __init__(self): 8 | self.scenarios = [] 9 | 10 | def scenario(self, id_, title="", owners=None): 11 | scenario = _scenario.Scenario(self, id_, title, owners) 12 | self.scenarios.append(scenario) 13 | return scenario 14 | 15 | @property 16 | def status(self): 17 | if all([(item.status == "skipped") for item in self.scenarios]): 18 | status = "skipped" 19 | elif any([(item.status == "failed") for item in self.scenarios]): 20 | status = "failed" 21 | else: 22 | status = "passed" 23 | return status 24 | 25 | @property 26 | def start_time(self): 27 | times = [ 28 | item.start_time 29 | for item in self.scenarios 30 | if item.status != "skipped" 31 | ] 32 | return min(times) if times else 0 33 | 34 | @property 35 | def end_time(self): 36 | times = [ 37 | item.end_time for item in self.scenarios if item.status != "skipped" 38 | ] 39 | return max(times) if times else 0 40 | 41 | def json(self): 42 | return { 43 | "status": self.status, 44 | "execution": { 45 | "duration": self.end_time - self.start_time, 46 | "start_time": self.start_time, 47 | "end_time": self.end_time, 48 | }, 49 | "scenarios": [item.json() for item in self.scenarios], 50 | "passed_count": len( 51 | [(1) for item in self.scenarios if item.status == "passed"] 52 | ), 53 | "failed_count": len( 54 | [(1) for item in self.scenarios if item.status == "failed"] 55 | ), 56 | "skipped_count": len( 57 | [(1) for item in self.scenarios if item.status == "skipped"] 58 | ), 59 | } 60 | -------------------------------------------------------------------------------- /ucloud/testing/funcs.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import datetime 4 | import time 5 | 6 | 7 | def concat(*args): 8 | """ cancat strings 9 | 10 | >>> concat(42, 'foo', 'bar') 11 | '42foobar' 12 | """ 13 | return "".join([str(s) for s in args]) 14 | 15 | 16 | def concat_without_dot(args): 17 | """ replace blank 18 | 19 | >>> concat_without_dot('42foo bar') 20 | '42foobar' 21 | """ 22 | return "".join([str(s) for s in args.split()]) 23 | 24 | 25 | def search_value(array, origin_key, origin_value, dest_key): 26 | """ given origin key and value,search dest_value form array by dest_key 27 | 28 | >>> d = [{"UHostId": "foo", "Name": "testing"}] 29 | >>> search_value(d, "Name", "testing", "UHostId") 30 | 'foo' 31 | """ 32 | arr = [i.get(dest_key, "") for i in array if i[origin_key] == origin_value] 33 | if arr: 34 | return arr[0] 35 | return "" 36 | 37 | 38 | def timedelta(timestamp, value, typ="days"): 39 | """ given timestamp(10bit) and calculate relative delta time 40 | 41 | >>> timedelta(0, 1, "days") 42 | 86400.0 43 | 44 | :param timestamp: timestamp (round to second) 45 | :param value: float, can as positive or negative 46 | :param typ: days/hours 47 | :return: timestamp 48 | """ 49 | value = int(value) 50 | dt = datetime.datetime.fromtimestamp(float(timestamp)) 51 | if typ == "days": 52 | dt += datetime.timedelta(days=value) 53 | elif typ == "hours": 54 | dt += datetime.timedelta(hours=value) 55 | return time.mktime(dt.timetuple()) 56 | 57 | 58 | def get_timestamp(length=13): 59 | """ get current timestamp string 60 | 61 | >>> len(str(int(get_timestamp(10)))) 62 | 10 63 | 64 | :param length: length of timestamp, can only between 0 and 16 65 | :return: 66 | """ 67 | if isinstance(length, int) and 0 < length < 17: 68 | return int("{:.6f}".format(time.time()).replace(".", "")[:length]) 69 | raise ValueError("timestamp length can only between 0 and 16.") 70 | -------------------------------------------------------------------------------- /docs/services.rst: -------------------------------------------------------------------------------- 1 | UCloud SDK Services 2 | =================== 3 | 4 | PathX 5 | ----- 6 | 7 | .. autoclass:: ucloud.services.pathx.client.PathXClient 8 | :members: 9 | 10 | StepFlow 11 | -------- 12 | 13 | .. autoclass:: ucloud.services.stepflow.client.StepFlowClient 14 | :members: 15 | 16 | UAccount 17 | -------- 18 | 19 | .. autoclass:: ucloud.services.uaccount.client.UAccountClient 20 | :members: 21 | 22 | UCDN 23 | ---- 24 | 25 | .. autoclass:: ucloud.services.ucdn.client.UCDNClient 26 | :members: 27 | 28 | UDB 29 | --- 30 | 31 | .. autoclass:: ucloud.services.udb.client.UDBClient 32 | :members: 33 | 34 | UDPN 35 | ---- 36 | 37 | .. autoclass:: ucloud.services.udpn.client.UDPNClient 38 | :members: 39 | 40 | UDisk 41 | ----- 42 | 43 | .. autoclass:: ucloud.services.udisk.client.UDiskClient 44 | :members: 45 | 46 | UHost 47 | ----- 48 | 49 | .. autoclass:: ucloud.services.uhost.client.UHostClient 50 | :members: 51 | 52 | ULB 53 | --- 54 | 55 | .. autoclass:: ucloud.services.ulb.client.ULBClient 56 | :members: 57 | 58 | UMem 59 | ---- 60 | 61 | .. autoclass:: ucloud.services.umem.client.UMemClient 62 | :members: 63 | 64 | UNet 65 | ---- 66 | 67 | .. autoclass:: ucloud.services.unet.client.UNetClient 68 | :members: 69 | 70 | UPHost 71 | ------ 72 | 73 | .. autoclass:: ucloud.services.uphost.client.UPHostClient 74 | :members: 75 | 76 | USMS 77 | ---- 78 | 79 | .. autoclass:: ucloud.services.usms.client.USMSClient 80 | :members: 81 | 82 | VPC 83 | --- 84 | 85 | .. autoclass:: ucloud.services.vpc.client.VPCClient 86 | :members: 87 | 88 | 89 | 90 | IPSecVPN 91 | -------- 92 | 93 | .. autoclass:: ucloud.services.ipsecvpn.client.IPSecVPNClient 94 | :members: 95 | 96 | 97 | UCloudStack 98 | ----------- 99 | 100 | .. autoclass:: ucloud.services.ucloudstack.client.UCloudStackClient 101 | :members: 102 | 103 | 104 | UFS 105 | --- 106 | 107 | .. autoclass:: ucloud.services.ufs.client.UFSClient 108 | :members: 109 | 110 | 111 | UHub 112 | ---- 113 | 114 | .. autoclass:: ucloud.services.uhub.client.UHubClient 115 | :members: 116 | 117 | -------------------------------------------------------------------------------- /tests/test_core/test_transport.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import pytest 4 | import logging 5 | from ucloud.core.transport import RequestsTransport, Request, Response, utils 6 | 7 | logger = logging.getLogger(__name__) 8 | 9 | 10 | @pytest.fixture(scope="function", autouse=True) 11 | def transport(): 12 | return RequestsTransport() 13 | 14 | 15 | class TestTransport(object): 16 | def test_transport_send(self, transport): 17 | req = Request( 18 | url="http://httpbin.org/anything", 19 | method="post", 20 | json={"foo": "bar"}, 21 | ) 22 | resp = transport.send(req) 23 | assert resp.text 24 | assert resp.json()["json"] == {"foo": "bar"} 25 | 26 | def test_transport_handler(self, transport): 27 | global_env = {} 28 | 29 | def request_handler(r): 30 | global_env["req"] = r 31 | return r 32 | 33 | def response_handler(r): 34 | global_env["resp"] = r 35 | return r 36 | 37 | transport.middleware.request(handler=request_handler) 38 | transport.middleware.response(handler=response_handler) 39 | req = Request( 40 | url="http://httpbin.org/anything", 41 | method="post", 42 | json={"foo": "bar"}, 43 | ) 44 | resp = transport.send(req) 45 | assert resp.text 46 | assert resp.json()["json"] == {"foo": "bar"} 47 | assert "req" in global_env 48 | assert "resp" in global_env 49 | 50 | 51 | class TestResponse(object): 52 | def test_guess_json_utf(self): 53 | import json 54 | 55 | encodings = [ 56 | "utf-32", 57 | "utf-8-sig", 58 | "utf-16", 59 | "utf-8", 60 | "utf-16-be", 61 | "utf-16-le", 62 | "utf-32-be", 63 | "utf-32-le", 64 | ] 65 | for e in encodings: 66 | s = json.dumps("表意字符").encode(e) 67 | assert utils.guess_json_utf(s) == e 68 | 69 | def test_response_empty_content(self): 70 | r = Response("http://foo.bar", "post") 71 | assert not r.text 72 | assert r.json() is None 73 | -------------------------------------------------------------------------------- /tests/test_unit/test_core/test_transport.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import pytest 4 | import logging 5 | from ucloud.core.transport import RequestsTransport, Request, Response, utils 6 | 7 | logger = logging.getLogger(__name__) 8 | 9 | 10 | @pytest.fixture(scope="function", autouse=True) 11 | def transport(): 12 | return RequestsTransport() 13 | 14 | 15 | class TestTransport(object): 16 | def test_transport_send(self, transport): 17 | req = Request( 18 | url="http://httpbin.org/anything", 19 | method="post", 20 | json={"foo": "bar"}, 21 | ) 22 | resp = transport.send(req) 23 | assert resp.text 24 | assert resp.json()["json"] == {"foo": "bar"} 25 | 26 | def test_transport_handler(self, transport): 27 | global_env = {} 28 | 29 | def request_handler(r): 30 | global_env["req"] = r 31 | return r 32 | 33 | def response_handler(r): 34 | global_env["resp"] = r 35 | return r 36 | 37 | transport.middleware.request(handler=request_handler) 38 | transport.middleware.response(handler=response_handler) 39 | req = Request( 40 | url="http://httpbin.org/anything", 41 | method="post", 42 | json={"foo": "bar"}, 43 | ) 44 | resp = transport.send(req) 45 | assert resp.text 46 | assert resp.json()["json"] == {"foo": "bar"} 47 | assert "req" in global_env 48 | assert "resp" in global_env 49 | 50 | 51 | class TestResponse(object): 52 | def test_guess_json_utf(self): 53 | import json 54 | 55 | encodings = [ 56 | "utf-32", 57 | "utf-8-sig", 58 | "utf-16", 59 | "utf-8", 60 | "utf-16-be", 61 | "utf-16-le", 62 | "utf-32-be", 63 | "utf-32-le", 64 | ] 65 | for e in encodings: 66 | s = json.dumps("表意字符").encode(e) 67 | assert utils.guess_json_utf(s) == e 68 | 69 | def test_response_empty_content(self): 70 | r = Response("http://foo.bar", "post") 71 | assert not r.text 72 | assert r.json() is None 73 | -------------------------------------------------------------------------------- /ucloud/services/stepflow/schemas/apis.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ Code is generated by ucloud-model, DO NOT EDIT IT. """ 4 | from ucloud.core.typesystem import schema, fields 5 | from ucloud.services.stepflow.schemas import models 6 | 7 | """ StepFlow API Schema 8 | """ 9 | """ 10 | API: CreateSFWorkflowFromTemplate 11 | 12 | 导入工作流定义 13 | """ 14 | 15 | 16 | class CreateSFWorkflowFromTemplateRequestSchema(schema.RequestSchema): 17 | """ CreateSFWorkflowFromTemplate - 导入工作流定义 18 | """ 19 | 20 | fields = { 21 | "Namespace": fields.Str(required=True, dump_to="Namespace"), 22 | "ProjectId": fields.Str(required=False, dump_to="ProjectId"), 23 | "Region": fields.Str(required=True, dump_to="Region"), 24 | "Workflow": fields.Base64(required=True, dump_to="Workflow"), 25 | "WorkflowName": fields.Str(required=True, dump_to="WorkflowName"), 26 | } 27 | 28 | 29 | class CreateSFWorkflowFromTemplateResponseSchema(schema.ResponseSchema): 30 | """ CreateSFWorkflowFromTemplate - 导入工作流定义 31 | """ 32 | 33 | fields = { 34 | "Message": fields.Str(required=True, load_from="Message"), 35 | "Version": fields.Int(required=True, load_from="Version"), 36 | } 37 | 38 | 39 | """ 40 | API: GetSFWorkflowTemplate 41 | 42 | 导出工作流定义 43 | """ 44 | 45 | 46 | class GetSFWorkflowTemplateRequestSchema(schema.RequestSchema): 47 | """ GetSFWorkflowTemplate - 导出工作流定义 48 | """ 49 | 50 | fields = { 51 | "ProjectId": fields.Str(required=False, dump_to="ProjectId"), 52 | "Region": fields.Str(required=True, dump_to="Region"), 53 | "WorkflowId": fields.Str(required=True, dump_to="WorkflowId"), 54 | "WorkflowVersion": fields.Int( 55 | required=False, dump_to="WorkflowVersion" 56 | ), 57 | } 58 | 59 | 60 | class GetSFWorkflowTemplateResponseSchema(schema.ResponseSchema): 61 | """ GetSFWorkflowTemplate - 导出工作流定义 62 | """ 63 | 64 | fields = { 65 | "Message": fields.Str(required=False, load_from="Message"), 66 | "Version": fields.Int(required=True, load_from="Version"), 67 | "Workflow": models.WorkflowTemplateSchema(), 68 | "WorkflowId": fields.Str(required=True, load_from="WorkflowId"), 69 | } 70 | -------------------------------------------------------------------------------- /ucloud/services/usms/schemas/models.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ Code is generated by ucloud-model, DO NOT EDIT IT. """ 4 | from ucloud.core.typesystem import schema, fields 5 | 6 | 7 | class ReceiptPerPhoneSchema(schema.ResponseSchema): 8 | """ ReceiptPerPhone - 每个目的手机号的发送回执信息 9 | """ 10 | 11 | fields = { 12 | "CostCount": fields.Int(required=True, load_from="CostCount"), 13 | "Phone": fields.Str(required=True, load_from="Phone"), 14 | "ReceiptDesc": fields.Str(required=True, load_from="ReceiptDesc"), 15 | "ReceiptResult": fields.Str(required=True, load_from="ReceiptResult"), 16 | "ReceiptTime": fields.Int(required=True, load_from="ReceiptTime"), 17 | } 18 | 19 | 20 | class ReceiptPerSessionSchema(schema.ResponseSchema): 21 | """ ReceiptPerSession - 每个提交的回执结果集合 22 | """ 23 | 24 | fields = { 25 | "ReceiptSet": fields.List(ReceiptPerPhoneSchema()), 26 | "SessionNo": fields.Str(required=True, load_from="SessionNo"), 27 | } 28 | 29 | 30 | class OutSignatureSchema(schema.ResponseSchema): 31 | """ OutSignature - 短信签名 32 | """ 33 | 34 | fields = { 35 | "ErrDesc": fields.Str(required=True, load_from="ErrDesc"), 36 | "SigContent": fields.Str(required=True, load_from="SigContent"), 37 | "SigId": fields.Str(required=True, load_from="SigId"), 38 | "Status": fields.Int(required=True, load_from="Status"), 39 | } 40 | 41 | 42 | class OutTemplateSchema(schema.ResponseSchema): 43 | """ OutTemplate - 短信模板 44 | """ 45 | 46 | fields = { 47 | "CreateTime": fields.Int(required=True, load_from="CreateTime"), 48 | "ErrDesc": fields.Str(required=True, load_from="ErrDesc"), 49 | "Purpose": fields.Int(required=True, load_from="Purpose"), 50 | "Remark": fields.Str(required=True, load_from="Remark"), 51 | "Status": fields.Int(required=True, load_from="Status"), 52 | "Template": fields.Str(required=True, load_from="Template"), 53 | "TemplateId": fields.Str(required=True, load_from="TemplateId"), 54 | "TemplateName": fields.Str(required=True, load_from="TemplateName"), 55 | "UnsubscribeInfo": fields.Str( 56 | required=True, load_from="UnsubscribeInfo" 57 | ), 58 | } 59 | -------------------------------------------------------------------------------- /ucloud/services/ipsecvpn/schemas/apis.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ Code is generated by ucloud-model, DO NOT EDIT IT. """ 4 | from ucloud.core.typesystem import schema, fields 5 | from ucloud.services.ipsecvpn.schemas import models 6 | 7 | """ IPSecVPN API Schema 8 | """ 9 | """ 10 | API: DescribeRemoteVPNGateway 11 | 12 | 获取客户VPN网关信息 13 | """ 14 | 15 | 16 | class DescribeRemoteVPNGatewayRequestSchema(schema.RequestSchema): 17 | """ DescribeRemoteVPNGateway - 获取客户VPN网关信息 18 | """ 19 | 20 | fields = { 21 | "Limit": fields.Int(required=False, dump_to="Limit"), 22 | "Offset": fields.Int(required=False, dump_to="Offset"), 23 | "ProjectId": fields.Str(required=True, dump_to="ProjectId"), 24 | "Region": fields.Str(required=True, dump_to="Region"), 25 | "RemoteVPNGatewayIds": fields.List(fields.Str()), 26 | "Tag": fields.Str(required=False, dump_to="Tag"), 27 | } 28 | 29 | 30 | class DescribeRemoteVPNGatewayResponseSchema(schema.ResponseSchema): 31 | """ DescribeRemoteVPNGateway - 获取客户VPN网关信息 32 | """ 33 | 34 | fields = { 35 | "DataSet": fields.List( 36 | models.RemoteVPNGatewayDataSetSchema(), 37 | required=False, 38 | load_from="DataSet", 39 | ), 40 | "TotalCount": fields.Int(required=False, load_from="TotalCount"), 41 | } 42 | 43 | 44 | """ 45 | API: DescribeVPNTunnel 46 | 47 | 获取VPN隧道信息 48 | """ 49 | 50 | 51 | class DescribeVPNTunnelRequestSchema(schema.RequestSchema): 52 | """ DescribeVPNTunnel - 获取VPN隧道信息 53 | """ 54 | 55 | fields = { 56 | "Limit": fields.Int(required=False, dump_to="Limit"), 57 | "Offset": fields.Int(required=False, dump_to="Offset"), 58 | "ProjectId": fields.Str(required=True, dump_to="ProjectId"), 59 | "Region": fields.Str(required=True, dump_to="Region"), 60 | "Tag": fields.Str(required=False, dump_to="Tag"), 61 | "VPNTunnelIds": fields.List(fields.Str()), 62 | } 63 | 64 | 65 | class DescribeVPNTunnelResponseSchema(schema.ResponseSchema): 66 | """ DescribeVPNTunnel - 获取VPN隧道信息 67 | """ 68 | 69 | fields = { 70 | "DataSet": fields.List( 71 | models.VPNTunnelDataSetSchema(), required=False, load_from="DataSet" 72 | ), 73 | "TotalCount": fields.Int(required=False, load_from="TotalCount"), 74 | } 75 | -------------------------------------------------------------------------------- /ucloud/core/auth/_cfg.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import hashlib 4 | from collections import OrderedDict 5 | from ucloud.core.typesystem import schema, fields, encoder 6 | 7 | 8 | class CredentialSchema(schema.Schema): 9 | fields = { 10 | "public_key": fields.Str(required=True), 11 | "private_key": fields.Str(required=True), 12 | } 13 | 14 | 15 | def verify_ac(private_key, params): 16 | """ calculate signature by private_key/public_key 17 | 18 | the keys can be found on `APIKey documentation `__ 19 | 20 | >>> verify_ac("my_private_key", {"foo": "bar"}) 21 | '634edc1bb957c0d65e5ab5494cf3b7784fbc87af' 22 | 23 | >>> verify_ac("my_private_key", {"foo": "bar"}) 24 | '634edc1bb957c0d65e5ab5494cf3b7784fbc87af' 25 | 26 | :param private_key: private key 27 | :param params: 28 | :return: 29 | """ 30 | params = OrderedDict(sorted(params.items(), key=lambda item: item[0])) 31 | simplified = "" 32 | for key, value in params.items(): 33 | simplified += str(key) + encoder.encode_value(value) 34 | simplified += private_key 35 | hash_new = hashlib.sha1() 36 | hash_new.update(simplified.encode("utf-8")) 37 | hash_value = hash_new.hexdigest() 38 | return hash_value 39 | 40 | 41 | class Credential(object): 42 | """ credential is the object to store credential information 43 | 44 | the keys can be found on `APIKey documentation `__ 45 | 46 | it can calculate signature for OpenAPI: 47 | 48 | >>> cred = Credential('my_public_key', 'my_private_key') 49 | >>> cred.verify_ac({"foo": "bar"}) 50 | 'd4411ab30953fb0bbcb1e7313081f05e4e91a394' 51 | 52 | :param public_key: 53 | :param private_key: 54 | """ 55 | 56 | PUBLIC_KEY_NAME = "PublicKey" 57 | 58 | def __init__(self, public_key, private_key, **kwargs): 59 | self.public_key = public_key 60 | self.private_key = private_key 61 | 62 | def verify_ac(self, args): 63 | args[Credential.PUBLIC_KEY_NAME] = self.public_key 64 | return verify_ac(self.private_key, args) 65 | 66 | @classmethod 67 | def from_dict(cls, d): 68 | parsed = CredentialSchema().dumps(d) 69 | return cls(**parsed) 70 | 71 | def to_dict(self): 72 | return {"public_key": self.public_key, "private_key": self.private_key} 73 | -------------------------------------------------------------------------------- /ucloud/core/utils/middleware.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | 4 | class Middleware(object): 5 | """ middleware is the object to store request/response handlers 6 | 7 | >>> middleware = Middleware() 8 | 9 | Add a request handler to prepare the request 10 | 11 | >>> @middleware.request 12 | ... def prepare(req): 13 | ... req['Region'] = 'cn-bj2' 14 | ... return req 15 | 16 | Add a response handler to log the response detail 17 | 18 | >>> @middleware.response 19 | ... def logged(resp): 20 | ... print(resp) 21 | ... return resp 22 | 23 | >>> len(middleware.request_handlers), len(middleware.response_handlers) 24 | (1, 1) 25 | """ 26 | 27 | def __init__(self): 28 | self.request_handlers = [] 29 | self.response_handlers = [] 30 | self.exception_handlers = [] 31 | 32 | def request(self, handler, index=-1): 33 | """ request is the request handler register to add request handler. 34 | 35 | :param handler: request handler function, receive request object 36 | and return a new request 37 | :param int index: the position of request in the handler list, 38 | default is append it to end 39 | :return: 40 | """ 41 | self.request_handlers.insert(index, handler) 42 | return handler 43 | 44 | def response(self, handler, index=-1): 45 | """ response is the response handler register to add response handler. 46 | 47 | :param handler: response handler function, receive response object 48 | and return a new response 49 | :param int index: the position of response in the handler list, 50 | default is append it to end 51 | :return: 52 | """ 53 | self.response_handlers.insert(index, handler) 54 | return handler 55 | 56 | def exception(self, handler, index=-1): 57 | """ exception is the exception handler register to add exception handler. 58 | 59 | :param handler: exception handler function, receive exception object 60 | and raise a new exception or ignore it 61 | :param int index: the position of handler in the handler list, 62 | default is append it to end 63 | :return: 64 | """ 65 | self.exception_handlers.insert(index, handler) 66 | return handler 67 | -------------------------------------------------------------------------------- /ucloud/helpers/utils.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import random 4 | import base64 5 | 6 | _lowercase = "abcdefghijklmnopqrstuvwxyz" 7 | _uppercase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 8 | _numbers = "0123456789" 9 | _specials = "_" 10 | 11 | 12 | def gen_password( 13 | n, 14 | lower_letters=_lowercase, 15 | upper_letters=_uppercase, 16 | number_letters=_numbers, 17 | special_letters=_specials, 18 | min_lower=1, 19 | min_upper=1, 20 | min_number=1, 21 | min_specials=1, 22 | ): 23 | """ generate password for any resource 24 | 25 | >>> len(gen_password(20)) 26 | 20 27 | 28 | :param int n: password total length 29 | :param str lower_letters: all lowercase letters 30 | :param str upper_letters: all uppercase letters 31 | :param str number_letters: all number letters 32 | :param str special_letters: all special letters 33 | :param int min_lower: minimal number of lowercase letters 34 | :param int min_upper: minimal number of uppercase letters 35 | :param int min_number: minimal number of number letters 36 | :param int min_specials: minimal number of special letters 37 | :return: 38 | """ 39 | all_letters = "".join( 40 | [lower_letters, upper_letters, number_letters, special_letters] 41 | ) 42 | minimal_total = min_lower + min_upper + min_number + min_specials 43 | if n < minimal_total: 44 | raise ValueError( 45 | "the length of password must be larger than total minimal letters number" 46 | ) 47 | minimal_letters = "".join( 48 | [ 49 | gen_string(lower_letters, min_lower), 50 | gen_string(upper_letters, min_upper), 51 | gen_string(number_letters, min_number), 52 | gen_string(special_letters, min_specials), 53 | ] 54 | ) 55 | additional_letters = random.sample(all_letters, n - minimal_total) 56 | results = list(minimal_letters) + additional_letters 57 | random.shuffle(results) 58 | return "".join(results) 59 | 60 | 61 | def gen_string(letters, length): 62 | return "".join([random.choice(letters) for i in range(length)]) 63 | 64 | 65 | def first(arr): 66 | if len(arr) == 0: 67 | return None 68 | return arr[0] 69 | 70 | 71 | def b64encode(s): 72 | """ base64 encode 73 | 74 | :param str s: input string 75 | :return: base64 string 76 | """ 77 | return base64.b64encode(s.encode()).decode() 78 | 79 | 80 | def b64decode(s): 81 | """ base64 decode 82 | 83 | :param str s: base64 string 84 | :return: output string 85 | """ 86 | return base64.b64decode(s.encode()).decode() 87 | -------------------------------------------------------------------------------- /tests/test_unit/test_core/test_client.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import os 4 | import pytest 5 | import logging 6 | from ucloud.client import Client 7 | from ucloud.core import exc 8 | from ucloud.testing.mock import MockedTransport 9 | 10 | logger = logging.getLogger(__name__) 11 | 12 | 13 | @pytest.fixture(scope="session", autouse=True) 14 | def client(): 15 | return Client( 16 | { 17 | "region": "cn-bj2", 18 | "public_key": "foo", 19 | "private_key": "foo", 20 | "timeout": 10, 21 | "max_retries": 3, 22 | "ssl_verify": False, 23 | } 24 | ) 25 | 26 | 27 | @pytest.fixture(scope="function", autouse=True) 28 | def transport(): 29 | return MockedTransport() 30 | 31 | 32 | class TestClient(object): 33 | def test_client_invoke(self, client, transport): 34 | transport.mock_data(lambda _: {"RetCode": 0, "Action": "Foo"}) 35 | client.transport = transport 36 | assert client.invoke("Foo") == {"RetCode": 0, "Action": "Foo"} 37 | 38 | def test_client_invoke_code_error(self, client, transport): 39 | transport.mock_data(lambda _: {"RetCode": 171, "Action": "Foo"}) 40 | client.transport = transport 41 | with pytest.raises(exc.RetCodeException): 42 | try: 43 | client.invoke("Foo") 44 | except exc.RetCodeException as e: 45 | assert str(e) 46 | expected = {"RetCode": 171, "Action": "Foo", "Message": ""} 47 | assert e.json() == expected 48 | raise e 49 | 50 | def test_client_invoke_with_retryable_error(self, client, transport): 51 | transport.mock_data(lambda _: {"RetCode": 10000, "Action": "Foo"}) 52 | client.transport = transport 53 | with pytest.raises(exc.RetCodeException): 54 | client.invoke("Foo") 55 | 56 | def test_client_invoke_with_unexpected_error(self, client, transport): 57 | def raise_error(_): 58 | raise ValueError("temporary error") 59 | 60 | transport.mock_data(raise_error) 61 | client.transport = transport 62 | with pytest.raises(ValueError): 63 | client.invoke("Foo") 64 | 65 | def test_client_try_import(self, client): 66 | assert client.pathx() 67 | assert client.stepflow() 68 | assert client.uaccount() 69 | assert client.udb() 70 | assert client.udpn() 71 | assert client.udisk() 72 | assert client.uhost() 73 | assert client.ulb() 74 | assert client.umem() 75 | assert client.unet() 76 | assert client.uphost() 77 | assert client.vpc() 78 | -------------------------------------------------------------------------------- /tests/test_core/test_client.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import os 4 | import pytest 5 | import logging 6 | from ucloud.client import Client 7 | from ucloud.core import exc 8 | from ucloud.testing.mock import MockedTransport 9 | 10 | logger = logging.getLogger(__name__) 11 | 12 | 13 | @pytest.fixture(scope="session", autouse=True) 14 | def client(): 15 | return Client( 16 | { 17 | "region": "cn-bj2", 18 | "public_key": "foo", 19 | "private_key": "foo", 20 | "timeout": 10, 21 | "max_retries": 3, 22 | } 23 | ) 24 | 25 | 26 | @pytest.fixture(scope="function", autouse=True) 27 | def transport(): 28 | return MockedTransport() 29 | 30 | 31 | class TestClient(object): 32 | def test_client_invoke(self, client, transport): 33 | transport.mock_data(lambda _: {"RetCode": 0, "Action": "Foo"}) 34 | client.transport = transport 35 | assert client.invoke("Foo") == {"RetCode": 0, "Action": "Foo"} 36 | 37 | def test_client_invoke_code_error(self, client, transport): 38 | transport.mock_data(lambda _: {"RetCode": 171, "Action": "Foo"}) 39 | client.transport = transport 40 | with pytest.raises(exc.RetCodeException): 41 | try: 42 | client.invoke("Foo") 43 | except exc.RetCodeException as e: 44 | assert str(e) 45 | expected = {"RetCode": 171, "Action": "Foo", "Message": ""} 46 | assert e.json() == expected 47 | raise e 48 | 49 | def test_client_invoke_with_retryable_error(self, client, transport): 50 | transport.mock_data(lambda _: {"RetCode": 10000, "Action": "Foo"}) 51 | client.transport = transport 52 | with pytest.raises(exc.RetCodeException): 53 | client.invoke("Foo") 54 | 55 | def test_client_invoke_with_unexpected_error(self, client, transport): 56 | def raise_error(_): 57 | raise ValueError("temporary error") 58 | 59 | transport.mock_data(raise_error) 60 | client.transport = transport 61 | with pytest.raises(ValueError): 62 | client.invoke("Foo") 63 | 64 | def test_client_try_import(self, client): 65 | assert client.pathx() 66 | assert client.stepflow() 67 | assert client.uaccount() 68 | assert client.udb() 69 | assert client.udpn() 70 | assert client.udisk() 71 | assert client.uhost() 72 | assert client.ulb() 73 | assert client.umem() 74 | assert client.unet() 75 | assert client.uphost() 76 | assert client.vpc() 77 | -------------------------------------------------------------------------------- /docs/usage.rst: -------------------------------------------------------------------------------- 1 | Usage 2 | ===== 3 | 4 | Type System 5 | ----------- 6 | 7 | UCloud Python SDK has type system for runtime checking. 8 | 9 | For example: 10 | 11 | .. code-block:: python 12 | 13 | client.uhost().create_uhost_instance({ 14 | 'CPU': "i am not a integer", 15 | }) 16 | 17 | it will raise :class:`ValidationException` with invalid integer and some required field is miss matched. 18 | 19 | Wait State Changed 20 | ------------------ 21 | 22 | SDK also provide state waiter helper to improver usage experience. 23 | 24 | **When using it?** 25 | 26 | Waiter can wait for remote state is achieved to target state. such as, 27 | 28 | - create and wait for resource state is completed. 29 | - invoke/start/stop a resource and wait for it is finished. 30 | - custom retry policies and wait for retrying is finished. 31 | 32 | For example: 33 | 34 | .. code-block:: python 35 | 36 | def mget_uhost_states(uhost_ids): 37 | resp = client.uhost().describe_uhost_instance({'UHostIds': uhost_ids}) 38 | return [inst.get('State') for inst in resp.get('UHostSet')] 39 | 40 | # Stop all instances 41 | for uhost_id in uhost_ids: 42 | client.uhost().stop_uhost_instance({'UHostId': uhost_id}) 43 | 44 | # Wait all instances is stopped 45 | wait.wait_for_state( 46 | target=['stopped'], pending=['pending'], 47 | timeout=300, # wait 5min 48 | refresh=lambda: ( 49 | 'stopped' if all([state == 'Stopped' for state in mget_uhost_states(uhost_ids)]) else 'pending' 50 | ), 51 | ) 52 | 53 | # Remove all instances 54 | for uhost_id in uhost_ids: 55 | client.uhost().terminate_uhost_instance({'UHostId': uhost_id}) 56 | 57 | By the default, waiter will use exponential back-off delay between twice request. 58 | it will raise :class:`WaitTimeoutException` when timeout is reached. 59 | 60 | Client/Transport Middleware 61 | --------------------------- 62 | 63 | UCloud SDK provide middleware feature to client or transport level request. 64 | 65 | It allowed to add custom logic into the lifecycle of request/response. 66 | 67 | For example: 68 | 69 | .. code-block:: python 70 | 71 | @client.middleware.request 72 | def log_params(req): 73 | print('[REQ]', req) 74 | 75 | @client.middleware.response 76 | def log_response(resp): 77 | print('[RESP]', resp) 78 | 79 | 80 | or transport: 81 | 82 | .. code-block:: python 83 | 84 | from ucloud.core.transport import RequestsTransport 85 | 86 | transport = RequestsTransport() 87 | 88 | @transport.middleware.request 89 | def log_request(req): 90 | print('[REQ]', req) 91 | 92 | @transport.middleware.response 93 | def log_response(resp): 94 | print('[RESP]', resp) 95 | 96 | Client({'Region': 'cn-bj2'}, transport=transport) 97 | -------------------------------------------------------------------------------- /ucloud/services/uaccount/schemas/models.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ Code is generated by ucloud-model, DO NOT EDIT IT. """ 4 | from ucloud.core.typesystem import schema, fields 5 | 6 | 7 | class ProjectListInfoSchema(schema.ResponseSchema): 8 | """ ProjectListInfo - 项目信息 9 | """ 10 | 11 | fields = { 12 | "CreateTime": fields.Int(required=True, load_from="CreateTime"), 13 | "IsDefault": fields.Bool(required=True, load_from="IsDefault"), 14 | "MemberCount": fields.Int(required=True, load_from="MemberCount"), 15 | "ParentId": fields.Str(required=True, load_from="ParentId"), 16 | "ParentName": fields.Str(required=True, load_from="ParentName"), 17 | "ProjectId": fields.Str(required=True, load_from="ProjectId"), 18 | "ProjectName": fields.Str(required=True, load_from="ProjectName"), 19 | "ResourceCount": fields.Int(required=True, load_from="ResourceCount"), 20 | } 21 | 22 | 23 | class RegionInfoSchema(schema.ResponseSchema): 24 | """ RegionInfo - 数据中心信息 25 | """ 26 | 27 | fields = { 28 | "BitMaps": fields.Str(required=True, load_from="BitMaps"), 29 | "IsDefault": fields.Bool(required=True, load_from="IsDefault"), 30 | "Region": fields.Str(required=True, load_from="Region"), 31 | "RegionId": fields.Int(required=True, load_from="RegionId"), 32 | "RegionName": fields.Str(required=True, load_from="RegionName"), 33 | "Zone": fields.Str(required=True, load_from="Zone"), 34 | } 35 | 36 | 37 | class UserInfoSchema(schema.ResponseSchema): 38 | """ UserInfo - 用户信息 39 | """ 40 | 41 | fields = { 42 | "Admin": fields.Int(required=True, load_from="Admin"), 43 | "Administrator": fields.Str(required=True, load_from="Administrator"), 44 | "AuthState": fields.Str(required=True, load_from="AuthState"), 45 | "City": fields.Str(required=True, load_from="City"), 46 | "CompanyName": fields.Str(required=True, load_from="CompanyName"), 47 | "Finance": fields.Int(required=True, load_from="Finance"), 48 | "IndustryType": fields.Int(required=True, load_from="IndustryType"), 49 | "PhonePrefix": fields.Str(required=True, load_from="PhonePrefix"), 50 | "Province": fields.Str(required=True, load_from="Province"), 51 | "UserAddress": fields.Str(required=True, load_from="UserAddress"), 52 | "UserEmail": fields.Str(required=True, load_from="UserEmail"), 53 | "UserId": fields.Int(required=True, load_from="UserId"), 54 | "UserName": fields.Str(required=True, load_from="UserName"), 55 | "UserPhone": fields.Str(required=True, load_from="UserPhone"), 56 | "UserType": fields.Int(required=True, load_from="UserType"), 57 | "UserVersion": fields.Int(required=True, load_from="UserVersion"), 58 | } 59 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: clean-pyc clean-build docs clean 2 | 3 | # Browser Tools 4 | define BROWSER_PYSCRIPT 5 | import os, webbrowser, sys 6 | try: 7 | from urllib import pathname2url 8 | except: 9 | from urllib.request import pathname2url 10 | 11 | webbrowser.open("file://" + pathname2url(os.path.abspath(sys.argv[1]))) 12 | endef 13 | export BROWSER_PYSCRIPT 14 | BROWSER := python -c "$$BROWSER_PYSCRIPT" 15 | 16 | # UCloud Tools Path 17 | UCLOUD_TEMPLATE_PATH=../ucloud-api-model-v2/apisdk/lang/python/templates 18 | 19 | help: 20 | @echo "clean - remove all build, test, coverage and Python artifacts" 21 | @echo "clean-build - remove build artifacts" 22 | @echo "clean-pyc - remove Python file artifacts" 23 | @echo "clean-test - remove test and coverage artifacts" 24 | @echo "lint - check style with flake8" 25 | @echo "test - run tests quickly with the default Python" 26 | @echo "test-all - run tests on every Python version with tox" 27 | @echo "coverage - check code coverage quickly with the default Python" 28 | @echo "docs - generate Sphinx HTML documentation, including API docs" 29 | @echo "release - package and upload a release" 30 | @echo "dist - package" 31 | @echo "install - install the package to the active Python's site-packages" 32 | 33 | # unit test 34 | test: clean 35 | pytest 36 | 37 | test-cov: clean 38 | pytest --cov=ucloud/core tests/test_core 39 | 40 | test-acc: clean 41 | USDKACC=1 pytest --cov=ucloud 42 | 43 | test-all: clean 44 | tox 45 | 46 | lint: 47 | @flake8 --exclude=ucloud/services ucloud --ignore=E501,F401 48 | 49 | fmt: 50 | @black -l 80 ucloud tests examples 51 | 52 | dev: 53 | @pip install -e .[dev] 54 | 55 | docs: 56 | #sphinx-apidoc -o docs/services ucloud/services 57 | $(MAKE) -C docs clean 58 | $(MAKE) -C docs html 59 | 60 | docs-preview: 61 | $(BROWSER) docs/_build/html/index.html 62 | 63 | clean: clean-build clean-pyc clean-test 64 | 65 | clean-build: 66 | rm -fr build/ 67 | rm -fr dist/ 68 | rm -fr .eggs/ 69 | 70 | clean-pyc: 71 | find . -name '*.pyc' -exec rm -f {} + 72 | find . -name '*.pyo' -exec rm -f {} + 73 | find . -name '*~' -exec rm -f {} + 74 | find . -name '__pycache__' -exec rm -fr {} + 75 | 76 | clean-test: 77 | rm -fr .tox/ 78 | rm -f .coverage 79 | rm -fr htmlcov/ 80 | 81 | migrate: 82 | git clone https://github.com/ucloud/ucloud-sdk-python3.git .migrate 83 | PYTHONPATH=. python3 scripts/migrate --source .migrate/ucloud --output ucloud 84 | PYTHONPATH=. python3 scripts/migrate --source .migrate/tests --output tests 85 | PYTHONPATH=. python3 scripts/migrate --source .migrate/docs --output docs 86 | PYTHONPATH=. python3 scripts/migrate --source .migrate/examples --output examples 87 | PYTHONPATH=. python3 scripts/migrate --source .migrate/README.md --output README.md 88 | sed -i 's/unicode/unicode # noqa: F821/g' ucloud/core/utils/compat.py 89 | rm -rf .migrate 90 | -------------------------------------------------------------------------------- /ucloud/core/typesystem/schema.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import logging 4 | from ucloud.core.typesystem import abstract 5 | from ucloud.core.exc import ValidationException 6 | from ucloud.core.utils import compat 7 | 8 | logger = logging.getLogger(__name__) 9 | 10 | 11 | class Schema(abstract.Schema): 12 | fields = {} 13 | 14 | def dumps(self, d, name=None, **kwargs): 15 | result = {} 16 | errors = [] 17 | for k, field in self.fields.items(): 18 | v = d.get(k) 19 | if v is None: 20 | if field.required: 21 | errors.append( 22 | ValidationException( 23 | "the field {k} is required".format(k=k) 24 | ) 25 | ) 26 | continue 27 | if field.default is None: 28 | continue 29 | if isinstance(field.default, compat.Callable): 30 | v = field.default() 31 | else: 32 | v = field.default 33 | try: 34 | serialized = field.dumps(v, name=k) 35 | except ValidationException as e: 36 | errors.extend(e.errors) 37 | continue 38 | result[field.dump_to or k] = serialized 39 | if len(errors) > 0: 40 | raise ValidationException(errors) 41 | return result 42 | 43 | def loads(self, d, name=None, **kwargs): 44 | result = {} 45 | errors = [] 46 | if not self.case_sensitive: 47 | d = {k.lower(): v for k, v in d.items()} 48 | for k, field in self.fields.items(): 49 | load_key = field.load_from or k 50 | v = d.get(load_key if self.case_sensitive else load_key.lower()) 51 | if v is None: 52 | if field.default is None: 53 | continue 54 | if isinstance(field.default, compat.Callable): 55 | v = field.default() 56 | else: 57 | v = field.default 58 | try: 59 | serialized = field.loads(v, name=k) 60 | except ValidationException as e: 61 | errors.extend(e.errors) 62 | continue 63 | result[k] = serialized 64 | if len(errors) > 0: 65 | raise ValidationException(errors) 66 | return result 67 | 68 | 69 | class RequestSchema(Schema): 70 | fields = {} 71 | 72 | def dumps(self, d, name=None, **kwargs): 73 | if not isinstance(d, dict): 74 | raise ValidationException( 75 | "invalid field {}, expect dict, got {}".format(name, type(d)) 76 | ) 77 | result = super(RequestSchema, self).dumps(d, name=name, **kwargs) 78 | return result 79 | 80 | 81 | class ResponseSchema(Schema): 82 | pass 83 | -------------------------------------------------------------------------------- /scripts/migrate/_migrate.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import itertools 3 | import os 4 | import typing 5 | import logging 6 | import dataclasses 7 | 8 | from scripts.migrate._plugin_py3to2 import SDK3to2Transformer 9 | from scripts.migrate._plugin_doc import DocTransformer 10 | 11 | logging.basicConfig() 12 | logger = logging.getLogger(__name__) 13 | 14 | 15 | @dataclasses.dataclass 16 | class PluginConfig: 17 | name: str 18 | ext: typing.List[str] = None 19 | options: dict = None 20 | 21 | 22 | class Config: 23 | paths: typing.List[str] = [] 24 | plugins: typing.List[PluginConfig] = [] 25 | 26 | 27 | class Migrate(object): 28 | default_plugins_classes = { 29 | "py3to2": SDK3to2Transformer, 30 | "doc": DocTransformer, 31 | } 32 | 33 | def __init__(self, config: Config = None): 34 | self.plugins = {} 35 | self.ext = {} 36 | for plugin in config.plugins: 37 | plugin_cls = self.default_plugins_classes.get(plugin.name) 38 | if plugin_cls is None: 39 | continue 40 | 41 | plugin_inst = plugin_cls(**(plugin.options or {})) 42 | self.plugins[plugin.name] = plugin_inst 43 | 44 | for ext in plugin.ext: 45 | self.ext.setdefault(ext, list()) 46 | self.ext[ext].append(plugin_inst) 47 | 48 | self.paths = config.paths 49 | 50 | def find_python_files(self, dir_or_file): 51 | if os.path.isdir(dir_or_file): 52 | for folder, _, files in list(os.walk(dir_or_file)): 53 | for file in files: 54 | yield folder + os.path.sep + file 55 | else: 56 | yield dir_or_file 57 | 58 | def run(self, source_path: str, output_path: str): 59 | for path in self.find_python_files(source_path): 60 | # find plugins by file extension 61 | plugins = [plugins for ext, plugins in self.ext.items() if path.endswith(ext)] 62 | plugins = plugins and plugins[0] 63 | 64 | # skip unexpected file by file extension 65 | if not plugins: 66 | continue 67 | 68 | # resolve path 69 | relative_path = os.path.relpath(path, source_path) 70 | result_path = os.path.abspath(os.path.join(output_path, relative_path)) 71 | print('migrate {} to {}'.format(path, result_path)) 72 | 73 | # read source code 74 | with open(path, encoding='utf-8') as f: 75 | source_code = f.read() 76 | 77 | # convert destination code 78 | output_code = source_code 79 | for plugin in plugins: 80 | output_code = plugin.convert(source_code) 81 | 82 | # output to file 83 | os.makedirs(os.path.dirname(result_path), exist_ok=True) 84 | 85 | with open(result_path, 'w', encoding='utf-8') as f: 86 | f.write(output_code) 87 | -------------------------------------------------------------------------------- /examples/uhost/main.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import os 4 | import logging 5 | import random 6 | from ucloud.client import Client 7 | from ucloud.helpers import wait, utils 8 | 9 | logger = logging.getLogger("ucloud") 10 | logger.setLevel(logging.DEBUG) 11 | 12 | 13 | def main(): 14 | client = Client( 15 | { 16 | "region": "cn-bj2", 17 | "project_id": os.getenv("UCLOUD_PROJECT_ID"), 18 | "public_key": os.getenv("UCLOUD_PUBLIC_KEY"), 19 | "private_key": os.getenv("UCLOUD_PRIVATE_KEY"), 20 | } 21 | ) 22 | logger.info("finding image, random choice one") 23 | images = ( 24 | client.uhost() 25 | .describe_image({"ImageType": "Base", "OsType": "Linux"}) 26 | .get("ImageSet", []) 27 | ) 28 | assert len(images) > 0 29 | logger.info("creating uhost instance ...") 30 | image = random.choice(images) 31 | resp = client.uhost().create_uhost_instance( 32 | { 33 | "Name": "sdk-python-example", 34 | "Zone": image["Zone"], 35 | "ImageId": image["ImageId"], 36 | "LoginMode": "Password", 37 | "Password": utils.gen_password(20), 38 | "CPU": 1, 39 | "Memory": 1024, 40 | "Disks": [ 41 | { 42 | "Size": image["ImageSize"], 43 | "Type": "LOCAL_NORMAL", 44 | "IsBoot": "True", 45 | } 46 | ], 47 | } 48 | ) 49 | uhost_id = utils.first(resp["UHostIds"]) 50 | logger.info("uhost instance is created") 51 | 52 | def refresh_state(): 53 | uhost = utils.first( 54 | client.uhost() 55 | .describe_uhost_instance({"UHostIds": [uhost_id]}) 56 | .get("UHostSet", []) 57 | ) 58 | if uhost.get("State") in ["Running", "Stopped"]: 59 | return uhost["State"].lower() 60 | return "pending" 61 | 62 | logger.info("wait uhost state is running ...") 63 | try: 64 | wait.wait_for_state( 65 | pending=["pending"], 66 | target=["running"], 67 | timeout=300, 68 | refresh=refresh_state, 69 | ) 70 | except wait.WaitTimeoutException as e: 71 | logger.error(e) 72 | logger.info("uhost instance is running") 73 | logger.info("stopping uhost for clean up resources ...") 74 | client.uhost().stop_uhost_instance({"UHostId": uhost_id}) 75 | try: 76 | wait.wait_for_state( 77 | pending=["pending"], 78 | target=["stopped"], 79 | timeout=180, 80 | refresh=refresh_state, 81 | ) 82 | except wait.WaitTimeoutException as e: 83 | logger.error(e) 84 | else: 85 | logger.info("uhost instance is stopped") 86 | logger.info("remove uhost instance from cloud") 87 | client.uhost().terminate_uhost_instance({"UHostId": uhost_id}) 88 | 89 | 90 | if __name__ == "__main__": 91 | main() 92 | -------------------------------------------------------------------------------- /ucloud/testing/driver/_scenario.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import functools 4 | from ucloud.testing.driver import _step 5 | 6 | 7 | class Scenario(object): 8 | def __init__(self, spec, id_, title=None, owners=None): 9 | self.id = id_ 10 | self.title = title 11 | self.store = {} 12 | self.errors = [] 13 | self.steps = [] 14 | self.spec = spec 15 | self.owners = owners 16 | 17 | def step(self, invoker, *args, **kwargs): 18 | step = _step.Step(self, invoker, *args, **kwargs) 19 | self.steps.append(step) 20 | return step 21 | 22 | def api(self, **step_kwargs): 23 | def deco(fn): 24 | step = self.step(fn, **step_kwargs) 25 | 26 | @functools.wraps(fn) 27 | def wrapped(*args, **kwargs): 28 | return step.run_api(*args, **kwargs) 29 | 30 | return wrapped 31 | 32 | return deco 33 | 34 | def func(self, **step_kwargs): 35 | def deco(fn): 36 | step = self.step(fn, **step_kwargs) 37 | 38 | @functools.wraps(fn) 39 | def wrapped(*args, **kwargs): 40 | return step.run_func(*args, **kwargs) 41 | 42 | return wrapped 43 | 44 | return deco 45 | 46 | @property 47 | def status(self): 48 | if all([(item.status == "skipped") for item in self.steps]): 49 | status = "skipped" 50 | elif any([(item.status == "failed") for item in self.steps]): 51 | status = "failed" 52 | else: 53 | status = "passed" 54 | return status 55 | 56 | @property 57 | def start_time(self): 58 | times = [ 59 | item.start_time for item in self.steps if item.status != "skipped" 60 | ] 61 | return min(times) if times else 0 62 | 63 | @property 64 | def end_time(self): 65 | times = [ 66 | item.end_time for item in self.steps if item.status != "skipped" 67 | ] 68 | return max(times) if times else 0 69 | 70 | def json(self): 71 | return { 72 | "title": self.title, 73 | "status": self.status, 74 | "steps": [item.json() for item in self.steps], 75 | "owners": self.owners, 76 | "execution": { 77 | "duration": self.end_time - self.start_time, 78 | "start_time": self.start_time, 79 | "end_time": self.end_time, 80 | }, 81 | "passed_count": len( 82 | [(1) for item in self.steps if item.status == "passed"] 83 | ), 84 | "failed_count": len( 85 | [(1) for item in self.steps if item.status == "failed"] 86 | ), 87 | "skipped_count": len( 88 | [(1) for item in self.steps if item.status == "skipped"] 89 | ), 90 | } 91 | 92 | def __call__(self, *args, **kwargs): 93 | for step in self.steps: 94 | step(step, *args, **kwargs) 95 | -------------------------------------------------------------------------------- /ucloud/core/transport/http.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import logging 4 | import json as json_mod 5 | from ucloud.core.transport import utils 6 | from ucloud.core.utils.compat import str 7 | 8 | logger = logging.getLogger(__name__) 9 | 10 | 11 | class Request(object): 12 | def __init__( 13 | self, 14 | url, 15 | method="GET", 16 | params=None, 17 | data=None, 18 | json=None, 19 | headers=None, 20 | **kwargs 21 | ): 22 | self.url = url 23 | self.method = method 24 | self.params = params 25 | self.data = data 26 | self.json = json 27 | self.headers = headers 28 | self.request_time = 0 29 | 30 | def payload(self): 31 | payload = (self.params or {}).copy() 32 | payload.update(self.data or {}) 33 | payload.update(self.json or {}) 34 | return payload 35 | 36 | 37 | class Response(object): 38 | def __init__( 39 | self, 40 | url, 41 | method, 42 | request=None, 43 | status_code=None, 44 | reason=None, 45 | headers=None, 46 | content=None, 47 | encoding=None, 48 | **kwargs 49 | ): 50 | self.url = url 51 | self.method = method 52 | self.request = request 53 | self.status_code = status_code 54 | self.reason = reason 55 | self.headers = headers 56 | self.content = content 57 | self.encoding = encoding 58 | self.response_time = 0 59 | 60 | def json(self, **kwargs): 61 | """ json will return the bytes of content 62 | """ 63 | if not self.content: 64 | return None 65 | encoding = utils.guess_json_utf(self.content) 66 | if encoding is not None: 67 | try: 68 | return json_mod.loads(self.content.decode(encoding), **kwargs) 69 | except UnicodeDecodeError: 70 | pass 71 | return json_mod.loads(self.text, **kwargs) 72 | 73 | @property 74 | def text(self): 75 | """ text will return the unicode string of content, 76 | see `requests.Response.text` 77 | """ 78 | if not self.content: 79 | return str("") 80 | try: 81 | content = str(self.content, self.encoding, errors="replace") 82 | except (LookupError, TypeError): 83 | content = str(self.content, errors="replace") 84 | return content 85 | 86 | 87 | class SSLOption(object): 88 | def __init__( 89 | self, ssl_verify=True, ssl_cacert=None, ssl_cert=None, ssl_key=None 90 | ): 91 | self.ssl_verify = ssl_verify 92 | self.ssl_cacert = ssl_cacert 93 | self.ssl_cert = ssl_cert 94 | self.ssl_key = ssl_key 95 | 96 | 97 | class Transport(object): 98 | """ the abstract class of transport implementation """ 99 | 100 | def send(self, req, **options): 101 | raise NotImplementedError 102 | -------------------------------------------------------------------------------- /ucloud/services/uaccount/schemas/apis.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ Code is generated by ucloud-model, DO NOT EDIT IT. """ 4 | from ucloud.core.typesystem import schema, fields 5 | from ucloud.services.uaccount.schemas import models 6 | 7 | """ UAccount API Schema 8 | """ 9 | """ 10 | API: CreateProject 11 | 12 | 创建项目 13 | """ 14 | 15 | 16 | class CreateProjectRequestSchema(schema.RequestSchema): 17 | """ CreateProject - 创建项目 18 | """ 19 | 20 | fields = {"ProjectName": fields.Str(required=True, dump_to="ProjectName")} 21 | 22 | 23 | class CreateProjectResponseSchema(schema.ResponseSchema): 24 | """ CreateProject - 创建项目 25 | """ 26 | 27 | fields = {"ProjectId": fields.Str(required=True, load_from="ProjectId")} 28 | 29 | 30 | """ 31 | API: GetProjectList 32 | 33 | 获取项目列表 34 | """ 35 | 36 | 37 | class GetProjectListRequestSchema(schema.RequestSchema): 38 | """ GetProjectList - 获取项目列表 39 | """ 40 | 41 | fields = {"IsFinance": fields.Str(required=False, dump_to="IsFinance")} 42 | 43 | 44 | class GetProjectListResponseSchema(schema.ResponseSchema): 45 | """ GetProjectList - 获取项目列表 46 | """ 47 | 48 | fields = { 49 | "ProjectCount": fields.Int(required=True, load_from="ProjectCount"), 50 | "ProjectSet": fields.List( 51 | models.ProjectListInfoSchema(), 52 | required=True, 53 | load_from="ProjectSet", 54 | ), 55 | } 56 | 57 | 58 | """ 59 | API: GetRegion 60 | 61 | 获取用户在各数据中心的权限等信息 62 | """ 63 | 64 | 65 | class GetRegionRequestSchema(schema.RequestSchema): 66 | """ GetRegion - 获取用户在各数据中心的权限等信息 67 | """ 68 | 69 | fields = {} 70 | 71 | 72 | class GetRegionResponseSchema(schema.ResponseSchema): 73 | """ GetRegion - 获取用户在各数据中心的权限等信息 74 | """ 75 | 76 | fields = { 77 | "Regions": fields.List( 78 | models.RegionInfoSchema(), required=False, load_from="Regions" 79 | ) 80 | } 81 | 82 | 83 | """ 84 | API: GetUserInfo 85 | 86 | 获取用户信息 87 | """ 88 | 89 | 90 | class GetUserInfoRequestSchema(schema.RequestSchema): 91 | """ GetUserInfo - 获取用户信息 92 | """ 93 | 94 | fields = {} 95 | 96 | 97 | class GetUserInfoResponseSchema(schema.ResponseSchema): 98 | """ GetUserInfo - 获取用户信息 99 | """ 100 | 101 | fields = { 102 | "DataSet": fields.List( 103 | models.UserInfoSchema(), required=True, load_from="DataSet" 104 | ) 105 | } 106 | 107 | 108 | """ 109 | API: ModifyProject 110 | 111 | 修改项目 112 | """ 113 | 114 | 115 | class ModifyProjectRequestSchema(schema.RequestSchema): 116 | """ ModifyProject - 修改项目 117 | """ 118 | 119 | fields = { 120 | "ProjectId": fields.Str(required=True, dump_to="ProjectId"), 121 | "ProjectName": fields.Str(required=True, dump_to="ProjectName"), 122 | } 123 | 124 | 125 | class ModifyProjectResponseSchema(schema.ResponseSchema): 126 | """ ModifyProject - 修改项目 127 | """ 128 | 129 | fields = {} 130 | 131 | 132 | """ 133 | API: TerminateProject 134 | 135 | 删除项目 136 | """ 137 | 138 | 139 | class TerminateProjectRequestSchema(schema.RequestSchema): 140 | """ TerminateProject - 删除项目 141 | """ 142 | 143 | fields = {"ProjectId": fields.Str(required=False, dump_to="ProjectId")} 144 | 145 | 146 | class TerminateProjectResponseSchema(schema.ResponseSchema): 147 | """ TerminateProject - 删除项目 148 | """ 149 | 150 | fields = {} 151 | -------------------------------------------------------------------------------- /ucloud/services/stepflow/client.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ Code is generated by ucloud-model, DO NOT EDIT IT. """ 4 | from ucloud.core.client import Client 5 | from ucloud.services.stepflow.schemas import apis 6 | 7 | 8 | class StepFlowClient(Client): 9 | def __init__(self, config, transport=None, middleware=None, logger=None): 10 | super(StepFlowClient, self).__init__( 11 | config, transport, middleware, logger 12 | ) 13 | 14 | def create_sf_workflow_from_template(self, req=None, **kwargs): 15 | """ CreateSFWorkflowFromTemplate - 导入工作流定义 16 | 17 | **Request** 18 | 19 | - **ProjectId** (str) - (Config) 项目ID。不填写为默认项目,子帐号必须填写。 请参考 `GetProjectList接口 `_ 20 | - **Region** (str) - (Config) 地域。 参见 `地域和可用区列表 `_ 21 | - **Namespace** (str) - (Required) 需要创建的工作流namespace 22 | - **Workflow** (str) - (Required) 描述工作流定义的base64字符串 23 | - **WorkflowName** (str) - (Required) 需要创建的工作流名称 24 | 25 | **Response** 26 | 27 | - **Message** (str) - 返回消息 28 | - **Version** (int) - 创建的工作流版本号 29 | 30 | """ 31 | d = {"ProjectId": self.config.project_id, "Region": self.config.region} 32 | req and d.update(req) 33 | d = apis.CreateSFWorkflowFromTemplateRequestSchema().dumps(d) 34 | kwargs["max_retries"] = 0 35 | resp = self.invoke("CreateSFWorkflowFromTemplate", d, **kwargs) 36 | return apis.CreateSFWorkflowFromTemplateResponseSchema().loads(resp) 37 | 38 | def get_sf_workflow_template(self, req=None, **kwargs): 39 | """ GetSFWorkflowTemplate - 导出工作流定义 40 | 41 | **Request** 42 | 43 | - **ProjectId** (str) - (Config) 项目ID。不填写为默认项目,子帐号必须填写。 请参考 `GetProjectList接口 `_ 44 | - **Region** (str) - (Config) 地域。 参见 `地域和可用区列表 `_ 45 | - **WorkflowId** (str) - (Required) 被导出工作流的Id 46 | - **WorkflowVersion** (int) - 被导出工作流的版本号。取值范围:WorkflowVersion >= 1;默认会获取发布版本对应的workflow;超过最大版本会返回错误 47 | 48 | **Response** 49 | 50 | - **Message** (str) - 返回消息 51 | - **Version** (int) - 导出工作流的版本号 52 | - **Workflow** (dict) - 见 **WorkflowTemplate** 模型定义 53 | - **WorkflowId** (str) - 导出工作流的Id 54 | 55 | **Response Model** 56 | 57 | **Param** 58 | 59 | - **Name** (str) - 参数名称 60 | - **Type** (str) - 参数类型 61 | - **Value** (str) - 参数值 62 | 63 | **ActivityTemplate** 64 | 65 | - **Input** (dict) - Activity的输入 66 | - **Name** (str) - Activity的名字 67 | - **Next** (str) - 下一个Activity的名字 68 | - **Output** (list) - Activity的输出,详见Param 69 | - **RetryTimes** (str) - Activity的重试次数 70 | - **Timeout** (str) - Activity的超时时间 71 | - **Type** (str) - Activity的类型 72 | 73 | **WorkflowTemplate** 74 | 75 | - **Activites** (list) - 见 **ActivityTemplate** 模型定义 76 | - **Input** (list) - 见 **Param** 模型定义 77 | - **Output** (list) - 见 **Param** 模型定义 78 | 79 | """ 80 | d = {"ProjectId": self.config.project_id, "Region": self.config.region} 81 | req and d.update(req) 82 | d = apis.GetSFWorkflowTemplateRequestSchema().dumps(d) 83 | resp = self.invoke("GetSFWorkflowTemplate", d, **kwargs) 84 | return apis.GetSFWorkflowTemplateResponseSchema().loads(resp) 85 | -------------------------------------------------------------------------------- /ucloud/helpers/wait.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import time 4 | import logging 5 | from ucloud.core import exc 6 | 7 | MAX_BACKOFF_INTERVAL = 10 8 | logger = logging.getLogger(__name__) 9 | 10 | 11 | class WaitTimeoutException(exc.UCloudException): 12 | pass 13 | 14 | 15 | class StateConf(object): 16 | """ StateConf is the utilities class to wait the state return by refresh function achieve the specific state, 17 | the generally usage is wait the cloud resource, such as uhost, udb ... is 18 | ready after created. 19 | """ 20 | 21 | def __init__( 22 | self, 23 | pending, 24 | target, 25 | refresh, 26 | timeout, 27 | startup_delay=0, 28 | min_backoff_interval=0.1, 29 | max_backoff_interval=MAX_BACKOFF_INTERVAL, 30 | ): 31 | self.pending = pending 32 | self.target = target 33 | self.refresh = refresh 34 | self.timeout = timeout 35 | self.startup_delay = startup_delay 36 | self.min_backoff_interval = min_backoff_interval 37 | self.max_backoff_interval = max_backoff_interval 38 | 39 | def wait(self): 40 | start_time = time.time() 41 | interval = self.min_backoff_interval 42 | time.sleep(self.startup_delay) 43 | while time.time() - start_time < self.timeout: 44 | state = self.refresh() 45 | if state in self.pending: 46 | time.sleep(interval) 47 | interval *= 2 48 | if interval > self.max_backoff_interval: 49 | interval = self.max_backoff_interval 50 | logger.info( 51 | "waiting state for {self.refresh}, got state {state}".format( 52 | self=self, state=state 53 | ) 54 | ) 55 | continue 56 | if state in self.target: 57 | return 58 | raise WaitTimeoutException( 59 | "wait timeout {self.timeout}s for {self.refresh}".format(self=self) 60 | ) 61 | 62 | 63 | def wait_for_state( 64 | pending, 65 | target, 66 | refresh, 67 | timeout, 68 | startup_delay=0, 69 | min_backoff_interval=0.1, 70 | max_backoff_interval=MAX_BACKOFF_INTERVAL, 71 | ): 72 | """ wait_for_state is a utilities function to wait the state return by refresh function achieve the specific state, 73 | the generally usage is wait the cloud resource, such as uhost, udb ... is 74 | ready after created. 75 | 76 | >>> wait_for_state( 77 | ... pending=["pending"], 78 | ... target=["running"], 79 | ... refresh=lambda: "running", 80 | ... timeout=0.5, 81 | ... ) 82 | 83 | :param pending: pending is the list of pending state, the state is returned by refresh function 84 | :param target: target is the list of target state, it is usually the terminate state, eg. running and fail 85 | :param refresh: the customized refresh function, expect no arguments and return a state 86 | :param timeout: timeout is the total time to wait state is achieved 87 | :param startup_delay: the time to wait before first refresh function is called 88 | :param min_backoff_interval: the backoff time for first refresh interval 89 | :param max_backoff_interval: the max backoff time for refresh interval 90 | """ 91 | conf = StateConf( 92 | pending=pending, 93 | target=target, 94 | refresh=refresh, 95 | timeout=timeout, 96 | startup_delay=startup_delay, 97 | min_backoff_interval=min_backoff_interval, 98 | max_backoff_interval=max_backoff_interval, 99 | ) 100 | return conf.wait() 101 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | 20 | from setuptools import setup, find_packages 21 | 22 | import importlib 23 | import io 24 | import logging 25 | import os 26 | import sys 27 | 28 | logger = logging.getLogger(__name__) 29 | 30 | PY2 = sys.version_info[0] == 2 and sys.version_info[1] >= 7 31 | 32 | if not PY2: 33 | raise NotImplementedError(( 34 | "ucloud-sdk-python2 should be used in 2.7" 35 | "and above of python interpreter" 36 | )) 37 | 38 | 39 | def load_version(): 40 | return importlib.import_module( 41 | "ucloud.version", os.path.join("ucloud", "version.py") 42 | ).version 43 | 44 | 45 | def load_long_description(): 46 | try: 47 | with io.open("README.md", encoding="utf-8") as f: 48 | return f.read() 49 | except IOError: 50 | return "" 51 | 52 | 53 | def load_requirements(requirements_file): 54 | try: 55 | with io.open(requirements_file, encoding="utf-8") as f: 56 | return list(f.readlines()) 57 | except IOError: 58 | return [] 59 | 60 | 61 | dependencies = load_requirements("requirements.txt") 62 | 63 | dependencies_test = dependencies + [ 64 | 'flake8>=3.6.0', 65 | 'pytest', 66 | 'pytest-cov', 67 | ] 68 | 69 | dependencies_doc = dependencies + ['sphinx'] 70 | 71 | dependencies_ci = list(set(dependencies_test + dependencies_doc)) 72 | 73 | dependencies_dev = list(set(dependencies_ci + ['black'])) 74 | 75 | 76 | def do_setup(): 77 | setup( 78 | name="ucloud-sdk-python2", 79 | description="UCloud Service Development Kit - Python", 80 | long_description=load_long_description(), 81 | long_description_content_type='text/markdown', 82 | license="Apache License 2.0", 83 | version=load_version(), 84 | packages=find_packages(exclude=["tests*"]), 85 | package_data={"": []}, 86 | include_package_data=True, 87 | zip_safe=False, 88 | install_requires=dependencies, 89 | extras_require={ 90 | "test": dependencies_test, 91 | "doc": dependencies_doc, 92 | "dev": dependencies_dev, 93 | "ci": dependencies_ci, 94 | }, 95 | classifiers=[ 96 | "Development Status :: 3 - Alpha", 97 | "Environment :: Console", 98 | "Environment :: Web Environment", 99 | "Intended Audience :: Developers", 100 | "Intended Audience :: System Administrators", 101 | "License :: OSI Approved :: Apache Software License", 102 | "Programming Language :: Python :: 2.7", 103 | "Programming Language :: Python :: 2 :: Only", 104 | "Topic :: Software Development", 105 | ], 106 | author="ucloud", 107 | author_email="esl_ipdd@ucloud.cn", 108 | url="https://github.com/ucloud/ucloud-sdk-python2", 109 | python_requires=">=2.7", 110 | ) 111 | 112 | 113 | if __name__ == "__main__": 114 | do_setup() 115 | -------------------------------------------------------------------------------- /ucloud/services/ufs/schemas/apis.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ Code is generated by ucloud-model, DO NOT EDIT IT. """ 4 | from ucloud.core.typesystem import schema, fields 5 | from ucloud.services.ufs.schemas import models 6 | 7 | """ UFS API Schema 8 | """ 9 | """ 10 | API: CreateUFSVolume 11 | 12 | 创建文件系统 13 | """ 14 | 15 | 16 | class CreateUFSVolumeRequestSchema(schema.RequestSchema): 17 | """ CreateUFSVolume - 创建文件系统 18 | """ 19 | 20 | fields = { 21 | "ChargeType": fields.Str(required=False, dump_to="ChargeType"), 22 | "CouponId": fields.Str(required=False, dump_to="CouponId"), 23 | "ProjectId": fields.Str(required=False, dump_to="ProjectId"), 24 | "ProtocolType": fields.Str(required=True, dump_to="ProtocolType"), 25 | "Quantity": fields.Int(required=False, dump_to="Quantity"), 26 | "Region": fields.Str(required=True, dump_to="Region"), 27 | "Remark": fields.Str(required=False, dump_to="Remark"), 28 | "Size": fields.Int(required=True, dump_to="Size"), 29 | "StorageType": fields.Str(required=True, dump_to="StorageType"), 30 | "Tag": fields.Str(required=False, dump_to="Tag"), 31 | "VolumeName": fields.Str(required=False, dump_to="VolumeName"), 32 | } 33 | 34 | 35 | class CreateUFSVolumeResponseSchema(schema.ResponseSchema): 36 | """ CreateUFSVolume - 创建文件系统 37 | """ 38 | 39 | fields = { 40 | "VolumeId": fields.Str(required=True, load_from="VolumeId"), 41 | "VolumeName": fields.Str(required=True, load_from="VolumeName"), 42 | "VolumeStatus": fields.Str(required=True, load_from="VolumeStatus"), 43 | } 44 | 45 | 46 | """ 47 | API: DescribeUFSVolume2 48 | 49 | 获取文件系统列表 50 | """ 51 | 52 | 53 | class DescribeUFSVolume2RequestSchema(schema.RequestSchema): 54 | """ DescribeUFSVolume2 - 获取文件系统列表 55 | """ 56 | 57 | fields = { 58 | "Limit": fields.Int(required=False, dump_to="Limit"), 59 | "Offset": fields.Int(required=False, dump_to="Offset"), 60 | "ProjectId": fields.Str(required=False, dump_to="ProjectId"), 61 | "Region": fields.Str(required=True, dump_to="Region"), 62 | "VolumeId": fields.Str(required=False, dump_to="VolumeId"), 63 | } 64 | 65 | 66 | class DescribeUFSVolume2ResponseSchema(schema.ResponseSchema): 67 | """ DescribeUFSVolume2 - 获取文件系统列表 68 | """ 69 | 70 | fields = { 71 | "DataSet": fields.List( 72 | models.UFSVolumeInfo2Schema(), required=True, load_from="DataSet" 73 | ), 74 | "TotalCount": fields.Int(required=True, load_from="TotalCount"), 75 | } 76 | 77 | 78 | """ 79 | API: ExtendUFSVolume 80 | 81 | 文件系统扩容 82 | """ 83 | 84 | 85 | class ExtendUFSVolumeRequestSchema(schema.RequestSchema): 86 | """ ExtendUFSVolume - 文件系统扩容 87 | """ 88 | 89 | fields = { 90 | "ProjectId": fields.Str(required=False, dump_to="ProjectId"), 91 | "Region": fields.Str(required=True, dump_to="Region"), 92 | "Size": fields.Int(required=True, dump_to="Size"), 93 | "VolumeId": fields.Str(required=True, dump_to="VolumeId"), 94 | } 95 | 96 | 97 | class ExtendUFSVolumeResponseSchema(schema.ResponseSchema): 98 | """ ExtendUFSVolume - 文件系统扩容 99 | """ 100 | 101 | fields = {} 102 | 103 | 104 | """ 105 | API: RemoveUFSVolume 106 | 107 | 删除UFS文件系统 108 | """ 109 | 110 | 111 | class RemoveUFSVolumeRequestSchema(schema.RequestSchema): 112 | """ RemoveUFSVolume - 删除UFS文件系统 113 | """ 114 | 115 | fields = { 116 | "ProjectId": fields.Str(required=False, dump_to="ProjectId"), 117 | "Region": fields.Str(required=True, dump_to="Region"), 118 | "VolumeId": fields.Str(required=True, dump_to="VolumeId"), 119 | } 120 | 121 | 122 | class RemoveUFSVolumeResponseSchema(schema.ResponseSchema): 123 | """ RemoveUFSVolume - 删除UFS文件系统 124 | """ 125 | 126 | fields = {} 127 | -------------------------------------------------------------------------------- /ucloud/core/client/_cfg.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import logging 4 | from ucloud.core.typesystem import schema, fields 5 | 6 | 7 | class ConfigSchema(schema.Schema): 8 | fields = { 9 | "region": fields.Str(), 10 | "project_id": fields.Str(), 11 | "base_url": fields.Str(default="https://api.ucloud.cn"), 12 | "user_agent": fields.Str(), 13 | "timeout": fields.Int(default=30), 14 | "max_retries": fields.Int(default=3), 15 | "log_level": fields.Int(default=logging.INFO), 16 | "validate_request": fields.Bool(default=True), 17 | "ssl_verify": fields.Bool(default=True), 18 | "ssl_cacert": fields.Str(), 19 | "ssl_cert": fields.Str(), 20 | "ssl_key": fields.Str(), 21 | } 22 | 23 | 24 | class Config(object): 25 | """ 26 | Config is the config of ucloud sdk, use for setting up 27 | 28 | :type region: str 29 | :param region: Region is the region of backend service, 30 | See also `Region list Documentation `_ 31 | :type project_id: str 32 | :param project_id: ProjectId is the unique identify of project, used for organize resources, 33 | Most of resources should belong to a project. Sub-Account must have an project id. 34 | See also `Project list Documentation `_ 35 | :type base_url: str 36 | :param base_url: BaseUrl is the url of backend api 37 | :param user_agent: UserAgent is an attribute for sdk client, used for distinguish who is using sdk. 38 | See also `User Agent `_ 39 | It will be appended to the end of sdk user-agent. 40 | eg. "MyAPP/0.10.1" -> "Python/3.7.0 Python-SDK/0.1.0 MyAPP/0.10.1" 41 | :type timeout: int 42 | :param timeout: Timeout is timeout for every request. 43 | :type max_retries: int 44 | :param max_retries: MaxRetries is the number of max retry times. 45 | Set MaxRetries more than 0 to enable auto-retry for network and service availability problem 46 | if auto-retry is enabled, it will enable default retry policy using exponential backoff. 47 | :type log_level: int 48 | :param log_level: LogLevel is equal to builtin logging level, 49 | if logLevel not be set, use INFO level as default. 50 | """ 51 | 52 | def __init__( 53 | self, 54 | region=None, 55 | project_id=None, 56 | base_url="https://api.ucloud.cn", 57 | user_agent=None, 58 | timeout=30, 59 | max_retries=3, 60 | log_level=logging.INFO, 61 | ssl_verify=True, 62 | ssl_cacert=None, 63 | ssl_cert=None, 64 | ssl_key=None, 65 | **kwargs 66 | ): 67 | self.region = region 68 | self.project_id = project_id 69 | self.base_url = base_url 70 | self.user_agent = user_agent 71 | self.timeout = timeout 72 | self.max_retries = max_retries 73 | self.log_level = log_level 74 | self.ssl_verify = ssl_verify 75 | self.ssl_cacert = ssl_cacert 76 | self.ssl_cert = ssl_cert 77 | self.ssl_key = ssl_key 78 | 79 | @classmethod 80 | def from_dict(cls, d): 81 | parsed = ConfigSchema().dumps(d) 82 | return cls(**parsed) 83 | 84 | def to_dict(self): 85 | return { 86 | "region": self.region, 87 | "project_id": self.project_id, 88 | "base_url": self.base_url, 89 | "user_agent": self.user_agent, 90 | "timeout": self.timeout, 91 | "max_retries": self.max_retries, 92 | "log_level": self.log_level, 93 | "ssl_verify": self.ssl_verify, 94 | "ssl_cacert": self.ssl_cacert, 95 | "ssl_cert": self.ssl_cert, 96 | "ssl_key": self.ssl_key, 97 | } 98 | -------------------------------------------------------------------------------- /examples/two-tier/main.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import os 4 | import logging 5 | import random 6 | from ucloud.client import Client 7 | from ucloud.helpers import wait, utils 8 | 9 | logger = logging.getLogger("ucloud") 10 | logger.setLevel(logging.DEBUG) 11 | client = Client( 12 | { 13 | "region": "cn-bj2", 14 | "project_id": os.getenv("UCLOUD_PROJECT_ID"), 15 | "public_key": os.getenv("UCLOUD_PUBLIC_KEY"), 16 | "private_key": os.getenv("UCLOUD_PRIVATE_KEY"), 17 | } 18 | ) 19 | 20 | 21 | def main(): 22 | image_id = describe_image() 23 | uhost_ids = create_uhost_batch(image_id, 2) 24 | ulb_id = create_ulb() 25 | vserver_id = create_vserver(ulb_id) 26 | backend_ids = allocate_backend_batch(ulb_id, vserver_id, uhost_ids) 27 | backend_ids and release_backend_batch(ulb_id, vserver_id, backend_ids) 28 | vserver_id and delete_vserver(ulb_id, vserver_id) 29 | ulb_id and delete_ulb(ulb_id) 30 | uhost_ids and delete_uhost_batch(uhost_ids) 31 | 32 | 33 | def describe_image(): 34 | images = ( 35 | client.uhost().describe_image({"ImageType": "Base"}).get("ImageSet", []) 36 | ) 37 | if not images: 38 | return 39 | image = random.choice(images) 40 | return image.get("ImageId") 41 | 42 | 43 | def mget_uhost_states(uhost_ids): 44 | resp = client.uhost().describe_uhost_instance({"UHostIds": uhost_ids}) 45 | return [inst.get("State") for inst in resp.get("UHostSet")] 46 | 47 | 48 | def create_uhost_batch(image_id, count): 49 | resp = client.uhost().create_uhost_instance( 50 | { 51 | "Name": "sdk-python3-example-two-tier", 52 | "Zone": "cn-bj2-05", 53 | "ImageId": image_id, 54 | "LoginMode": "Password", 55 | "Password": utils.gen_password(20), 56 | "CPU": 1, 57 | "Memory": 1024, 58 | "MaxCount": count, 59 | } 60 | ) 61 | uhost_ids = resp.get("UHostIds", []) 62 | wait.wait_for_state( 63 | target=["running"], 64 | pending=["pending"], 65 | timeout=300, 66 | refresh=lambda: "running" 67 | if all([(state == "Running") for state in mget_uhost_states(uhost_ids)]) 68 | else "pending", 69 | ) 70 | return uhost_ids 71 | 72 | 73 | def create_ulb(): 74 | resp = client.ulb().create_ulb({"Name": "sdk-python3-example-two-tier"}) 75 | return resp.get("ULBId") 76 | 77 | 78 | def create_vserver(ulb_id): 79 | resp = client.ulb().create_vserver( 80 | {"Name": "sdk-python3-example-two-tier", "ULBId": ulb_id} 81 | ) 82 | return resp.get("VServerId") 83 | 84 | 85 | def allocate_backend_batch(ulb_id, vserver_id, uhost_ids): 86 | backend_ids = [] 87 | for uhost_id in uhost_ids: 88 | resp = client.ulb().allocate_backend( 89 | { 90 | "ULBId": ulb_id, 91 | "VServerId": vserver_id, 92 | "ResourceId": uhost_id, 93 | "ResourceType": "UHost", 94 | } 95 | ) 96 | backend_ids.append(resp.get("BackendId")) 97 | return backend_ids 98 | 99 | 100 | def release_backend_batch(ulb_id, vserver_id, backend_ids): 101 | for backend_id in backend_ids: 102 | client.ulb().release_backend( 103 | {"ULBId": ulb_id, "VServerId": vserver_id, "BackendId": backend_id} 104 | ) 105 | 106 | 107 | def delete_vserver(ulb_id, vserver_id): 108 | client.ulb().delete_vserver({"ULBId": ulb_id, "VServerId": vserver_id}) 109 | 110 | 111 | def delete_ulb(ulb_id): 112 | client.ulb().delete_ulb({"ULBId": ulb_id}) 113 | 114 | 115 | def delete_uhost_batch(uhost_ids): 116 | for uhost_id in uhost_ids: 117 | client.uhost().stop_uhost_instance({"UHostId": uhost_id}) 118 | wait.wait_for_state( 119 | target=["stopped"], 120 | pending=["pending"], 121 | timeout=300, 122 | refresh=lambda: "stopped" 123 | if all([(state == "Stopped") for state in mget_uhost_states(uhost_ids)]) 124 | else "pending", 125 | ) 126 | for uhost_id in uhost_ids: 127 | client.uhost().terminate_uhost_instance({"UHostId": uhost_id}) 128 | 129 | 130 | if __name__ == "__main__": 131 | main() 132 | -------------------------------------------------------------------------------- /ucloud/services/udisk/schemas/models.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ Code is generated by ucloud-model, DO NOT EDIT IT. """ 4 | from ucloud.core.typesystem import schema, fields 5 | 6 | 7 | class UDiskDataSetSchema(schema.ResponseSchema): 8 | """ UDiskDataSet - DescribeUDisk 9 | """ 10 | 11 | fields = { 12 | "ArkSwitchEnable": fields.Int( 13 | required=False, load_from="ArkSwitchEnable" 14 | ), 15 | "ChargeType": fields.Str(required=False, load_from="ChargeType"), 16 | "CloneEnable": fields.Int(required=False, load_from="CloneEnable"), 17 | "CmkId": fields.Str(required=False, load_from="CmkId"), 18 | "CmkIdAlias": fields.Str(required=False, load_from="CmkIdAlias"), 19 | "CmkIdStatus": fields.Str(required=False, load_from="CmkIdStatus"), 20 | "CreateTime": fields.Int(required=False, load_from="CreateTime"), 21 | "DataKey": fields.Str(required=False, load_from="DataKey"), 22 | "DeviceName": fields.Str(required=False, load_from="DeviceName"), 23 | "DiskType": fields.Str(required=False, load_from="DiskType"), 24 | "ExpiredTime": fields.Int(required=False, load_from="ExpiredTime"), 25 | "IsBoot": fields.Str(required=False, load_from="IsBoot"), 26 | "IsExpire": fields.Str(required=False, load_from="IsExpire"), 27 | "Name": fields.Str(required=False, load_from="Name"), 28 | "Size": fields.Int(required=False, load_from="Size"), 29 | "SnapEnable": fields.Int(required=False, load_from="SnapEnable"), 30 | "SnapshotCount": fields.Int(required=False, load_from="SnapshotCount"), 31 | "SnapshotLimit": fields.Int(required=False, load_from="SnapshotLimit"), 32 | "Status": fields.Str(required=False, load_from="Status"), 33 | "Tag": fields.Str(required=False, load_from="Tag"), 34 | "UDataArkMode": fields.Str(required=False, load_from="UDataArkMode"), 35 | "UDiskId": fields.Str(required=False, load_from="UDiskId"), 36 | "UHostIP": fields.Str(required=False, load_from="UHostIP"), 37 | "UHostId": fields.Str(required=False, load_from="UHostId"), 38 | "UHostName": fields.Str(required=False, load_from="UHostName"), 39 | "UKmsMode": fields.Str(required=False, load_from="UKmsMode"), 40 | "Version": fields.Str(required=False, load_from="Version"), 41 | "Zone": fields.Str(required=False, load_from="Zone"), 42 | } 43 | 44 | 45 | class UDiskPriceDataSetSchema(schema.ResponseSchema): 46 | """ UDiskPriceDataSet - DescribeUDiskPrice 47 | """ 48 | 49 | fields = { 50 | "ChargeName": fields.Str(required=False, load_from="ChargeName"), 51 | "ChargeType": fields.Str(required=False, load_from="ChargeType"), 52 | "OriginalPrice": fields.Int(required=False, load_from="OriginalPrice"), 53 | "Price": fields.Int(required=False, load_from="Price"), 54 | } 55 | 56 | 57 | class UDiskSnapshotSetSchema(schema.ResponseSchema): 58 | """ UDiskSnapshotSet - DescribeUDiskSnapshot 59 | """ 60 | 61 | fields = { 62 | "CmkId": fields.Str(required=False, load_from="CmkId"), 63 | "CmkIdAlias": fields.Str(required=False, load_from="CmkIdAlias"), 64 | "CmkIdStatus": fields.Str(required=False, load_from="CmkIdStatus"), 65 | "Comment": fields.Str(required=False, load_from="Comment"), 66 | "CreateTime": fields.Int(required=True, load_from="CreateTime"), 67 | "DataKey": fields.Str(required=False, load_from="DataKey"), 68 | "DiskType": fields.Int(required=True, load_from="DiskType"), 69 | "ExpiredTime": fields.Int(required=False, load_from="ExpiredTime"), 70 | "IsUDiskAvailable": fields.Bool( 71 | required=False, load_from="IsUDiskAvailable" 72 | ), 73 | "Name": fields.Str(required=True, load_from="Name"), 74 | "Size": fields.Int(required=True, load_from="Size"), 75 | "SnapshotId": fields.Str(required=True, load_from="SnapshotId"), 76 | "Status": fields.Str(required=True, load_from="Status"), 77 | "UDiskId": fields.Str(required=True, load_from="UDiskId"), 78 | "UDiskName": fields.Str(required=True, load_from="UDiskName"), 79 | "UHostId": fields.Str(required=False, load_from="UHostId"), 80 | "UKmsMode": fields.Str(required=False, load_from="UKmsMode"), 81 | "Version": fields.Str(required=False, load_from="Version"), 82 | } 83 | -------------------------------------------------------------------------------- /ucloud/services/ipsecvpn/schemas/models.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ Code is generated by ucloud-model, DO NOT EDIT IT. """ 4 | from ucloud.core.typesystem import schema, fields 5 | 6 | 7 | class RemoteVPNGatewayDataSetSchema(schema.ResponseSchema): 8 | """ RemoteVPNGatewayDataSet - DescribeRemoteVPNGateway返回参数 9 | """ 10 | 11 | fields = { 12 | "ActiveTunnels": fields.Str(required=False, load_from="ActiveTunnels"), 13 | "CreateTime": fields.Int(required=False, load_from="CreateTime"), 14 | "Remark": fields.Str(required=False, load_from="Remark"), 15 | "RemoteVPNGatewayAddr": fields.Str( 16 | required=False, load_from="RemoteVPNGatewayAddr" 17 | ), 18 | "RemoteVPNGatewayId": fields.Str( 19 | required=False, load_from="RemoteVPNGatewayId" 20 | ), 21 | "RemoteVPNGatewayName": fields.Str( 22 | required=False, load_from="RemoteVPNGatewayName" 23 | ), 24 | "Tag": fields.Str(required=False, load_from="Tag"), 25 | "TunnelCount": fields.Int(required=False, load_from="TunnelCount"), 26 | } 27 | 28 | 29 | class IPSecDataSchema(schema.ResponseSchema): 30 | """ IPSecData - IPSec参数 31 | """ 32 | 33 | fields = { 34 | "IPSecAuthenticationAlgorithm": fields.Str( 35 | required=False, load_from="IPSecAuthenticationAlgorithm" 36 | ), 37 | "IPSecEncryptionAlgorithm": fields.Str( 38 | required=False, load_from="IPSecEncryptionAlgorithm" 39 | ), 40 | "IPSecLocalSubnetIds": fields.List(fields.Str()), 41 | "IPSecPFSDhGroup": fields.Str( 42 | required=False, load_from="IPSecPFSDhGroup" 43 | ), 44 | "IPSecProtocol": fields.Str(required=False, load_from="IPSecProtocol"), 45 | "IPSecRemoteSubnets": fields.List(fields.Str()), 46 | "IPSecSALifetime": fields.Str( 47 | required=False, load_from="IPSecSALifetime" 48 | ), 49 | "IPSecSALifetimeBytes": fields.Str( 50 | required=False, load_from="IPSecSALifetimeBytes" 51 | ), 52 | } 53 | 54 | 55 | class IKEDataSchema(schema.ResponseSchema): 56 | """ IKEData - IKE信息 57 | """ 58 | 59 | fields = { 60 | "IKEAuthenticationAlgorithm": fields.Str( 61 | required=False, load_from="IKEAuthenticationAlgorithm" 62 | ), 63 | "IKEDhGroup": fields.Str(required=False, load_from="IKEDhGroup"), 64 | "IKEEncryptionAlgorithm": fields.Str( 65 | required=False, load_from="IKEEncryptionAlgorithm" 66 | ), 67 | "IKEExchangeMode": fields.Str( 68 | required=False, load_from="IKEExchangeMode" 69 | ), 70 | "IKELocalId": fields.Str(required=False, load_from="IKELocalId"), 71 | "IKEPreSharedKey": fields.Str( 72 | required=False, load_from="IKEPreSharedKey" 73 | ), 74 | "IKERemoteId": fields.Str(required=False, load_from="IKERemoteId"), 75 | "IKESALifetime": fields.Str(required=False, load_from="IKESALifetime"), 76 | "IKEVersion": fields.Str(required=False, load_from="IKEVersion"), 77 | } 78 | 79 | 80 | class VPNTunnelDataSetSchema(schema.ResponseSchema): 81 | """ VPNTunnelDataSet - DescribeVPNTunnel信息 82 | """ 83 | 84 | fields = { 85 | "CreateTime": fields.Int(required=False, load_from="CreateTime"), 86 | "IKEData": IKEDataSchema(), 87 | "IPSecData": IPSecDataSchema(), 88 | "Remark": fields.Str(required=False, load_from="Remark"), 89 | "RemoteVPNGatewayId": fields.Str( 90 | required=False, load_from="RemoteVPNGatewayId" 91 | ), 92 | "RemoteVPNGatewayName": fields.Str( 93 | required=False, load_from="RemoteVPNGatewayName" 94 | ), 95 | "Tag": fields.Str(required=False, load_from="Tag"), 96 | "VPCId": fields.Str(required=False, load_from="VPCId"), 97 | "VPCName": fields.Str(required=False, load_from="VPCName"), 98 | "VPNGatewayId": fields.Str(required=False, load_from="VPNGatewayId"), 99 | "VPNGatewayName": fields.Str( 100 | required=False, load_from="VPNGatewayName" 101 | ), 102 | "VPNTunnelId": fields.Str(required=False, load_from="VPNTunnelId"), 103 | "VPNTunnelName": fields.Str(required=False, load_from="VPNTunnelName"), 104 | } 105 | -------------------------------------------------------------------------------- /ucloud/client.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ Code is generated by ucloud-model, DO NOT EDIT IT. """ 4 | from ucloud._compat import CompactClient 5 | 6 | 7 | class Client(CompactClient): 8 | def __init__(self, config, transport=None, middleware=None): 9 | self._config = config 10 | super(Client, self).__init__(config, transport, middleware) 11 | 12 | def pathx(self): 13 | from ucloud.services.pathx.client import PathXClient 14 | 15 | return PathXClient( 16 | self._config, self.transport, self.middleware, self.logger 17 | ) 18 | 19 | def stepflow(self): 20 | from ucloud.services.stepflow.client import StepFlowClient 21 | 22 | return StepFlowClient( 23 | self._config, self.transport, self.middleware, self.logger 24 | ) 25 | 26 | def uaccount(self): 27 | from ucloud.services.uaccount.client import UAccountClient 28 | 29 | return UAccountClient( 30 | self._config, self.transport, self.middleware, self.logger 31 | ) 32 | 33 | def ucdn(self): 34 | from ucloud.services.ucdn.client import UCDNClient 35 | 36 | return UCDNClient( 37 | self._config, self.transport, self.middleware, self.logger 38 | ) 39 | 40 | def udb(self): 41 | from ucloud.services.udb.client import UDBClient 42 | 43 | return UDBClient( 44 | self._config, self.transport, self.middleware, self.logger 45 | ) 46 | 47 | def udpn(self): 48 | from ucloud.services.udpn.client import UDPNClient 49 | 50 | return UDPNClient( 51 | self._config, self.transport, self.middleware, self.logger 52 | ) 53 | 54 | def udisk(self): 55 | from ucloud.services.udisk.client import UDiskClient 56 | 57 | return UDiskClient( 58 | self._config, self.transport, self.middleware, self.logger 59 | ) 60 | 61 | def uhost(self): 62 | from ucloud.services.uhost.client import UHostClient 63 | 64 | return UHostClient( 65 | self._config, self.transport, self.middleware, self.logger 66 | ) 67 | 68 | def ulb(self): 69 | from ucloud.services.ulb.client import ULBClient 70 | 71 | return ULBClient( 72 | self._config, self.transport, self.middleware, self.logger 73 | ) 74 | 75 | def umem(self): 76 | from ucloud.services.umem.client import UMemClient 77 | 78 | return UMemClient( 79 | self._config, self.transport, self.middleware, self.logger 80 | ) 81 | 82 | def unet(self): 83 | from ucloud.services.unet.client import UNetClient 84 | 85 | return UNetClient( 86 | self._config, self.transport, self.middleware, self.logger 87 | ) 88 | 89 | def uphost(self): 90 | from ucloud.services.uphost.client import UPHostClient 91 | 92 | return UPHostClient( 93 | self._config, self.transport, self.middleware, self.logger 94 | ) 95 | 96 | def usms(self): 97 | from ucloud.services.usms.client import USMSClient 98 | 99 | return USMSClient( 100 | self._config, self.transport, self.middleware, self.logger 101 | ) 102 | 103 | def ipsecvpn(self): 104 | from ucloud.services.ipsecvpn.client import IPSecVPNClient 105 | 106 | return IPSecVPNClient( 107 | self._config, self.transport, self.middleware, self.logger 108 | ) 109 | 110 | def ucloudstack(self): 111 | from ucloud.services.ucloudstack.client import UCloudStackClient 112 | 113 | return UCloudStackClient( 114 | self._config, self.transport, self.middleware, self.logger 115 | ) 116 | 117 | def ufs(self): 118 | from ucloud.services.ufs.client import UFSClient 119 | 120 | return UFSClient( 121 | self._config, self.transport, self.middleware, self.logger 122 | ) 123 | 124 | def uhub(self): 125 | from ucloud.services.uhub.client import UHubClient 126 | 127 | return UHubClient( 128 | self._config, self.transport, self.middleware, self.logger 129 | ) 130 | 131 | def vpc(self): 132 | from ucloud.services.vpc.client import VPCClient 133 | 134 | return VPCClient( 135 | self._config, self.transport, self.middleware, self.logger 136 | ) 137 | -------------------------------------------------------------------------------- /tests/test_services/test_set_2935.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ Code is generated by ucloud-model, DO NOT EDIT IT. """ 4 | import pytest 5 | import logging 6 | from ucloud.core import exc 7 | from ucloud.testing import env, funcs, op, utest 8 | 9 | logger = logging.getLogger(__name__) 10 | scenario = utest.Scenario(2935) 11 | 12 | 13 | @pytest.mark.skipif(env.is_ut(), reason=env.get_skip_reason()) 14 | def test_set_2935(client, variables): 15 | scenario.initial(variables) 16 | scenario.variables[ 17 | "Image_Id_ucloud" 18 | ] = "#{u_get_image_resource($Region,$Zone)}" 19 | scenario.variables["saopaulo_image"] = "uimage-1bkjka" 20 | scenario.run(client) 21 | 22 | 23 | @scenario.step( 24 | max_retries=3, 25 | retry_interval=1, 26 | startup_delay=0, 27 | fast_fail=False, 28 | validators=lambda variables: [ 29 | ("str_eq", "RetCode", 0), 30 | ("str_eq", "Action", "DescribeImageResponse"), 31 | ], 32 | action="DescribeImage", 33 | ) 34 | def describe_image_00(client, variables): 35 | d = { 36 | "Zone": variables.get("Zone"), 37 | "Region": variables.get("Region"), 38 | "OsType": "Linux", 39 | "ImageType": "Base", 40 | } 41 | try: 42 | resp = client.uhost().describe_image(d) 43 | except exc.RetCodeException as e: 44 | resp = e.json() 45 | variables["Image_Id_ucloud"] = utest.value_at_path( 46 | resp, "ImageSet.0.ImageId" 47 | ) 48 | return resp 49 | 50 | 51 | @scenario.step( 52 | max_retries=10, 53 | retry_interval=10, 54 | startup_delay=0, 55 | fast_fail=False, 56 | validators=lambda variables: [("str_eq", "RetCode", 0)], 57 | action="CreateULB", 58 | ) 59 | def create_ulb_01(client, variables): 60 | d = { 61 | "ULBName": "测试", 62 | "Tag": "Default", 63 | "Region": variables.get("Region"), 64 | "InnerMode": "No", 65 | } 66 | try: 67 | resp = client.ulb().create_ulb(d) 68 | except exc.RetCodeException as e: 69 | resp = e.json() 70 | variables["ULBId"] = utest.value_at_path(resp, "ULBId") 71 | return resp 72 | 73 | 74 | @scenario.step( 75 | max_retries=10, 76 | retry_interval=10, 77 | startup_delay=5, 78 | fast_fail=False, 79 | validators=lambda variables: [ 80 | ("str_eq", "RetCode", 0), 81 | ("len_ge", "DataSet", 1), 82 | ], 83 | action="DescribeULBSimple", 84 | ) 85 | def describe_ulb_simple_02(client, variables): 86 | d = {"Region": variables.get("Region")} 87 | try: 88 | resp = client.invoke("DescribeULBSimple", d) 89 | except exc.RetCodeException as e: 90 | resp = e.json() 91 | return resp 92 | 93 | 94 | @scenario.step( 95 | max_retries=10, 96 | retry_interval=1, 97 | startup_delay=0, 98 | fast_fail=False, 99 | validators=lambda variables: [ 100 | ("str_eq", "RetCode", 0), 101 | ("len_eq", "DataSet", 1), 102 | ], 103 | action="DescribeULBSimple", 104 | ) 105 | def describe_ulb_simple_03(client, variables): 106 | d = {"ULBId": variables.get("ULBId"), "Region": variables.get("Region")} 107 | try: 108 | resp = client.invoke("DescribeULBSimple", d) 109 | except exc.RetCodeException as e: 110 | resp = e.json() 111 | return resp 112 | 113 | 114 | @scenario.step( 115 | max_retries=10, 116 | retry_interval=10, 117 | startup_delay=3, 118 | fast_fail=False, 119 | validators=lambda variables: [ 120 | ("str_eq", "RetCode", 0), 121 | ("str_eq", "DataSet.0.ULBId", variables.get("ULBId")), 122 | ], 123 | action="DescribeULB", 124 | ) 125 | def describe_ulb_04(client, variables): 126 | d = { 127 | "ULBId": variables.get("ULBId"), 128 | "Region": variables.get("Region"), 129 | "Offset": 0, 130 | "Limit": 60, 131 | } 132 | try: 133 | resp = client.ulb().describe_ulb(d) 134 | except exc.RetCodeException as e: 135 | resp = e.json() 136 | return resp 137 | 138 | 139 | @scenario.step( 140 | max_retries=10, 141 | retry_interval=10, 142 | startup_delay=0, 143 | fast_fail=False, 144 | validators=lambda variables: [("str_eq", "RetCode", 0)], 145 | action="DeleteULB", 146 | ) 147 | def delete_ulb_05(client, variables): 148 | d = {"ULBId": variables.get("ULBId"), "Region": variables.get("Region")} 149 | try: 150 | resp = client.ulb().delete_ulb(d) 151 | except exc.RetCodeException as e: 152 | resp = e.json() 153 | return resp 154 | -------------------------------------------------------------------------------- /ucloud/core/typesystem/fields.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import base64 4 | import collections 5 | from ucloud.core.typesystem import abstract 6 | from ucloud.core.exc import ValidationException 7 | from ucloud.core.utils.compat import str 8 | 9 | 10 | class List(abstract.Field): 11 | """ array param is the custom field to parse custom param such as: 12 | 13 | - IP.N 14 | - UDisk.N.Size 15 | - NetInterface.N.EIP.Bandwidth 16 | """ 17 | 18 | def __init__(self, item, default=list, **kwargs): 19 | super(List, self).__init__(default=default, **kwargs) 20 | self.item = item 21 | 22 | def dumps(self, value, name=None, **kwargs): 23 | if not isinstance(value, collections.Iterable): 24 | raise ValidationException( 25 | "invalid field {}, expect list, got {}".format( 26 | name, type(value) 27 | ) 28 | ) 29 | errors = [] 30 | values = [] 31 | for each in value: 32 | try: 33 | v = self.item.dumps(each) 34 | except ValidationException as e: 35 | errors.extend(e.errors) 36 | else: 37 | values.append(v) 38 | if len(errors) > 0: 39 | raise ValidationException(errors) 40 | return values 41 | 42 | def loads(self, value, name=None, **kwargs): 43 | if not isinstance(value, collections.Iterable): 44 | raise ValidationException( 45 | "invalid field {}, expect list, got {}".format( 46 | name, type(value) 47 | ) 48 | ) 49 | errors = [] 50 | values = [] 51 | for each in value: 52 | try: 53 | v = self.item.loads(each) 54 | except ValidationException as e: 55 | errors.extend(e.errors) 56 | else: 57 | values.append(v) 58 | if len(errors) > 0: 59 | raise ValidationException(errors) 60 | return values 61 | 62 | 63 | class Str(abstract.Field): 64 | def dumps(self, value, name=None, **kwargs): 65 | return self._convert(value, name) 66 | 67 | def loads(self, value, name=None, **kwargs): 68 | return self._convert(value, name) 69 | 70 | def _convert(self, value, name=None): 71 | if self.strict and not isinstance(value, str): 72 | self.fail(name, "str", type(value)) 73 | return str(value) 74 | 75 | 76 | class Base64(Str): 77 | def dumps(self, value, name=None, **kwargs): 78 | s = super(Base64, self).dumps(value, name) 79 | return base64.b64encode(s.encode()).decode() 80 | 81 | def loads(self, value, name=None, **kwargs): 82 | s = super(Base64, self).loads(value, name) 83 | return base64.b64decode(s.encode()).decode() 84 | 85 | 86 | class Int(abstract.Field): 87 | def dumps(self, value, name=None, **kwargs): 88 | return self._convert(value, name) 89 | 90 | def loads(self, value, name=None, **kwargs): 91 | return self._convert(value, name) 92 | 93 | def _convert(self, value, name=None): 94 | if self.strict and not isinstance(value, int): 95 | self.fail(name, "int", type(value)) 96 | try: 97 | return int(value) 98 | except ValueError: 99 | self.fail(name, "int", type(value)) 100 | 101 | 102 | class Float(abstract.Field): 103 | def dumps(self, value, name=None, **kwargs): 104 | return self._convert(value, name) 105 | 106 | def loads(self, value, name=None, **kwargs): 107 | return self._convert(value, name) 108 | 109 | def _convert(self, value, name=None): 110 | if self.strict and not isinstance(value, float): 111 | self.fail(name, "float", type(value)) 112 | try: 113 | return float(value) 114 | except ValueError: 115 | self.fail(name, "float", type(value)) 116 | 117 | 118 | class Bool(abstract.Field): 119 | def dumps(self, value, name=None, **kwargs): 120 | return self._convert(value, name) 121 | 122 | def loads(self, value, name=None, **kwargs): 123 | return self._convert(value, name) 124 | 125 | def _convert(self, value, name=None): 126 | if self.strict and not isinstance(value, bool): 127 | self.fail(name, "bool", type(value)) 128 | if value == "true" or value is True: 129 | return True 130 | if value == "false" or value is False: 131 | return False 132 | self.fail(name, "bool", type(value)) 133 | -------------------------------------------------------------------------------- /ucloud/core/transport/_requests.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import time 4 | import requests 5 | from urllib3.util.retry import Retry 6 | from requests.adapters import HTTPAdapter 7 | from ucloud.core.transport import http 8 | from ucloud.core.transport.http import Request, Response, SSLOption 9 | from ucloud.core.utils.middleware import Middleware 10 | 11 | 12 | class RequestsTransport(http.Transport): 13 | """ transport is the implementation of http client, use for send a request and return a http response 14 | 15 | :type max_retries: int 16 | :param max_retries: max retries is the max number of transport request when occur http error 17 | :type backoff_factor: float 18 | :param backoff_factor: backoff factor will calculate the backoff delay during retrying, 19 | the backoff delay = {backoff factor} * (2 ^ ({number of total retries} - 1)) 20 | :type status_forcelist: tuple 21 | :param status_forcelist: the status code list that could be retried 22 | """ 23 | 24 | def __init__( 25 | self, 26 | max_retries=3, 27 | backoff_factor=0.3, 28 | status_forcelist=(500, 502, 504), 29 | ): 30 | self.max_retries = max_retries 31 | self.backoff_factor = backoff_factor 32 | self.status_forcelist = status_forcelist 33 | self._adapter = self._load_adapter(max_retries) 34 | self._middleware = Middleware() 35 | 36 | def send(self, req, **options): 37 | """ send request and return the response 38 | 39 | :param req: the full http request descriptor 40 | :return: the response of http request 41 | """ 42 | for handler in self.middleware.request_handlers: 43 | req = handler(req) 44 | try: 45 | resp = self._send(req, **options) 46 | except Exception as e: 47 | for handler in self.middleware.exception_handlers: 48 | handler(e) 49 | raise e 50 | for handler in self.middleware.response_handlers: 51 | resp = handler(resp) 52 | return resp 53 | 54 | @property 55 | def middleware(self): 56 | """ the middleware object, see :mod: 57 | 58 | :return: the transport middleware 59 | """ 60 | return self._middleware 61 | 62 | def _send(self, req, **options): 63 | with requests.Session() as session: 64 | adapter = self._load_adapter(options.get("max_retries")) 65 | session.mount("http://", adapter=adapter) 66 | session.mount("https://", adapter=adapter) 67 | ssl_option = options.get("ssl_option") 68 | kwargs = self._build_ssl_option(ssl_option) if ssl_option else {} 69 | req.request_time = time.time() 70 | session_resp = session.request( 71 | method=req.method.upper(), 72 | url=req.url, 73 | json=req.json, 74 | data=req.data, 75 | params=req.params, 76 | headers=req.headers, 77 | **kwargs 78 | ) 79 | resp = self.convert_response(session_resp) 80 | resp.request = req 81 | resp.response_time = time.time() 82 | return resp 83 | 84 | @staticmethod 85 | def _build_ssl_option(ssl_option): 86 | kwargs = {"verify": ssl_option.ssl_verify and ssl_option.ssl_cacert} 87 | if not ssl_option.ssl_cert: 88 | return kwargs 89 | if ssl_option.ssl_key: 90 | kwargs["cert"] = ssl_option.ssl_cert, ssl_option.ssl_key 91 | else: 92 | kwargs["cert"] = ssl_option.ssl_cert 93 | return kwargs 94 | 95 | def _load_adapter(self, max_retries=None): 96 | if max_retries is None and self._adapter is not None: 97 | return self._adapter 98 | max_retries = max_retries or 0 99 | adapter = HTTPAdapter() 100 | adapter.max_retries = Retry( 101 | total=max_retries, 102 | read=max_retries, 103 | connect=max_retries, 104 | backoff_factor=self.backoff_factor, 105 | status_forcelist=self.status_forcelist, 106 | ) 107 | return adapter 108 | 109 | @staticmethod 110 | def convert_response(r): 111 | return Response( 112 | url=r.url, 113 | method=r.request.method, 114 | status_code=r.status_code, 115 | reason=r.reason, 116 | headers=r.headers, 117 | content=r.content, 118 | encoding=r.encoding or r.apparent_encoding, 119 | ) 120 | -------------------------------------------------------------------------------- /ucloud/services/uphost/schemas/models.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ Code is generated by ucloud-model, DO NOT EDIT IT. """ 4 | from ucloud.core.typesystem import schema, fields 5 | 6 | 7 | class PHostIPSetSchema(schema.ResponseSchema): 8 | """ PHostIPSet - DescribePHost 9 | """ 10 | 11 | fields = { 12 | "Bandwidth": fields.Int(required=False, load_from="Bandwidth"), 13 | "IPAddr": fields.Str(required=False, load_from="IPAddr"), 14 | "IPId": fields.Str(required=False, load_from="IPId"), 15 | "MACAddr": fields.Str(required=False, load_from="MACAddr"), 16 | "OperatorName": fields.Str(required=False, load_from="OperatorName"), 17 | "SubnetId": fields.Str(required=False, load_from="SubnetId"), 18 | "VPCId": fields.Str(required=False, load_from="VPCId"), 19 | } 20 | 21 | 22 | class PHostDiskSetSchema(schema.ResponseSchema): 23 | """ PHostDiskSet - GetPHostTypeInfo 24 | """ 25 | 26 | fields = { 27 | "Count": fields.Int(required=False, load_from="Count"), 28 | "IOCap": fields.Int(required=False, load_from="IOCap"), 29 | "Name": fields.Str(required=False, load_from="Name"), 30 | "Space": fields.Int(required=False, load_from="Space"), 31 | "Type": fields.Str(required=False, load_from="Type"), 32 | } 33 | 34 | 35 | class PHostCPUSetSchema(schema.ResponseSchema): 36 | """ PHostCPUSet - DescribePHost 37 | """ 38 | 39 | fields = { 40 | "CoreCount": fields.Int(required=False, load_from="CoreCount"), 41 | "Count": fields.Int(required=False, load_from="Count"), 42 | "Frequence": fields.Float(required=False, load_from="Frequence"), 43 | "Model": fields.Str(required=False, load_from="Model"), 44 | } 45 | 46 | 47 | class PHostSetSchema(schema.ResponseSchema): 48 | """ PHostSet - DescribePHost 49 | """ 50 | 51 | fields = { 52 | "AutoRenew": fields.Str(required=False, load_from="AutoRenew"), 53 | "CPUSet": PHostCPUSetSchema(), 54 | "ChargeType": fields.Str(required=False, load_from="ChargeType"), 55 | "Cluster": fields.Str(required=False, load_from="Cluster"), 56 | "Components": fields.Str(required=False, load_from="Components"), 57 | "CreateTime": fields.Int(required=False, load_from="CreateTime"), 58 | "DiskSet": fields.List(PHostDiskSetSchema()), 59 | "ExpireTime": fields.Int(required=False, load_from="ExpireTime"), 60 | "IPSet": fields.List(PHostIPSetSchema()), 61 | "ImageName": fields.Str(required=False, load_from="ImageName"), 62 | "IsSupportKVM": fields.Str(required=False, load_from="IsSupportKVM"), 63 | "Memory": fields.Int(required=False, load_from="Memory"), 64 | "Name": fields.Str(required=False, load_from="Name"), 65 | "OSType": fields.Str(required=False, load_from="OSType"), 66 | "OSname": fields.Str(required=False, load_from="OSname"), 67 | "PHostId": fields.Str(required=False, load_from="PHostId"), 68 | "PHostType": fields.Str(required=False, load_from="PHostType"), 69 | "PMStatus": fields.Str(required=False, load_from="PMStatus"), 70 | "PowerState": fields.Str(required=False, load_from="PowerState"), 71 | "RaidSupported": fields.Str(required=False, load_from="RaidSupported"), 72 | "Remark": fields.Str(required=False, load_from="Remark"), 73 | "SN": fields.Str(required=False, load_from="SN"), 74 | "Tag": fields.Str(required=False, load_from="Tag"), 75 | "Zone": fields.Str(required=False, load_from="Zone"), 76 | } 77 | 78 | 79 | class PHostImageSetSchema(schema.ResponseSchema): 80 | """ PHostImageSet - DescribePHostImage 81 | """ 82 | 83 | fields = { 84 | "ImageId": fields.Str(required=False, load_from="ImageId"), 85 | "ImageName": fields.Str(required=False, load_from="ImageName"), 86 | "OsName": fields.Str(required=False, load_from="OsName"), 87 | "OsType": fields.Str(required=False, load_from="OsType"), 88 | } 89 | 90 | 91 | class PHostTagSetSchema(schema.ResponseSchema): 92 | """ PHostTagSet - DescribePHostTags 93 | """ 94 | 95 | fields = { 96 | "Tag": fields.Str(required=False, load_from="Tag"), 97 | "TotalCount": fields.Int(required=False, load_from="TotalCount"), 98 | } 99 | 100 | 101 | class PHostPriceSetSchema(schema.ResponseSchema): 102 | """ PHostPriceSet - GetPHostPrice 103 | """ 104 | 105 | fields = { 106 | "ChargeType": fields.Str(required=False, load_from="ChargeType"), 107 | "Price": fields.Float(required=False, load_from="Price"), 108 | } 109 | -------------------------------------------------------------------------------- /ucloud/services/ipsecvpn/client.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ Code is generated by ucloud-model, DO NOT EDIT IT. """ 4 | from ucloud.core.client import Client 5 | from ucloud.services.ipsecvpn.schemas import apis 6 | 7 | 8 | class IPSecVPNClient(Client): 9 | def __init__(self, config, transport=None, middleware=None, logger=None): 10 | super(IPSecVPNClient, self).__init__( 11 | config, transport, middleware, logger 12 | ) 13 | 14 | def describe_remote_vpn_gateway(self, req=None, **kwargs): 15 | """ DescribeRemoteVPNGateway - 获取客户VPN网关信息 16 | 17 | **Request** 18 | 19 | - **ProjectId** (str) - (Config) 项目ID。不填写为默认项目,子帐号必须填写。 请参考 `GetProjectList接口 `_ 20 | - **Region** (str) - (Config) 地域。 参见 `地域和可用区列表 `_ 21 | - **Limit** (int) - 数据分页值, 默认为20 22 | - **Offset** (int) - 数据偏移量, 默认为0 23 | - **RemoteVPNGatewayIds** (list) - 客户VPN网关的资源ID,例如RemoteVPNGatewayIds.0代表希望获取客户VPN网关1的信息,RemoteVPNGatewayIds.1代表客户VPN网关2,如果为空,则返回当前Region中所有客户VPN网关实例的信息 24 | - **Tag** (str) - 业务组名称,若指定则返回业务组下所有客户VPN网关信息 25 | 26 | **Response** 27 | 28 | - **DataSet** (list) - 见 **RemoteVPNGatewayDataSet** 模型定义 29 | - **TotalCount** (int) - 符合条件的客户VPN网关总数 30 | 31 | **Response Model** 32 | 33 | **RemoteVPNGatewayDataSet** 34 | 35 | - **ActiveTunnels** (str) - 活跃的隧道id 36 | - **CreateTime** (int) - 创建时间 37 | - **Remark** (str) - 备注 38 | - **RemoteVPNGatewayAddr** (str) - 客户网关IP地址 39 | - **RemoteVPNGatewayId** (str) - 客户网关ID 40 | - **RemoteVPNGatewayName** (str) - 客户网关名称 41 | - **Tag** (str) - 用户组 42 | - **TunnelCount** (int) - 活跃的隧道数量 43 | 44 | """ 45 | d = {"ProjectId": self.config.project_id, "Region": self.config.region} 46 | req and d.update(req) 47 | d = apis.DescribeRemoteVPNGatewayRequestSchema().dumps(d) 48 | resp = self.invoke("DescribeRemoteVPNGateway", d, **kwargs) 49 | return apis.DescribeRemoteVPNGatewayResponseSchema().loads(resp) 50 | 51 | def describe_vpn_tunnel(self, req=None, **kwargs): 52 | """ DescribeVPNTunnel - 获取VPN隧道信息 53 | 54 | **Request** 55 | 56 | - **ProjectId** (str) - (Config) 项目ID。不填写为默认项目,子帐号必须填写。 请参考 `GetProjectList接口 `_ 57 | - **Region** (str) - (Config) 地域。 参见 `地域和可用区列表 `_ 58 | - **Limit** (int) - 数据分页值, 默认为20 59 | - **Offset** (int) - 数据偏移量, 默认为0 60 | - **Tag** (str) - 业务组名称,若指定则返回指定的业务组下的所有VPN网关的信息 61 | - **VPNTunnelIds** (list) - VPN隧道的资源ID,例如VPNTunnelIds.0代表希望获取信息的VPN隧道1,VPNTunneIds.1代表VPN隧道2,如果为空,则返回当前Region中所有的VPN隧道实例 62 | 63 | **Response** 64 | 65 | - **DataSet** (list) - 见 **VPNTunnelDataSet** 模型定义 66 | - **TotalCount** (int) - VPN隧道总数 67 | 68 | **Response Model** 69 | 70 | **IPSecData** 71 | 72 | - **IPSecAuthenticationAlgorithm** (str) - IPSec通道中使用的认证算法 73 | - **IPSecEncryptionAlgorithm** (str) - IPSec通道中使用的加密算法 74 | - **IPSecLocalSubnetIds** (list) - 指定VPN连接的本地子网,用逗号分隔 75 | - **IPSecPFSDhGroup** (str) - 是否开启PFS功能,Disable表示关闭,数字表示DH组 76 | - **IPSecProtocol** (str) - 使用的安全协议,ESP或AH 77 | - **IPSecRemoteSubnets** (list) - 指定VPN连接的客户网段,用逗号分隔 78 | - **IPSecSALifetime** (str) - IPSec中SA的生存时间 79 | - **IPSecSALifetimeBytes** (str) - IPSec中SA的生存时间(以字节计) 80 | 81 | **IKEData** 82 | 83 | - **IKEAuthenticationAlgorithm** (str) - IKE认证算法 84 | - **IKEDhGroup** (str) - IKEDH组 85 | - **IKEEncryptionAlgorithm** (str) - IKE加密算法 86 | - **IKEExchangeMode** (str) - IKEv1协商模式 87 | - **IKELocalId** (str) - IKE本地ID标识 88 | - **IKEPreSharedKey** (str) - IKE预共享秘钥 89 | - **IKERemoteId** (str) - IKE对端ID标识 90 | - **IKESALifetime** (str) - IKE秘钥生存时间 91 | - **IKEVersion** (str) - IKE版本 92 | 93 | **VPNTunnelDataSet** 94 | 95 | - **CreateTime** (int) - 创建时间 96 | - **IKEData** (dict) - 见 **IKEData** 模型定义 97 | - **IPSecData** (dict) - 见 **IPSecData** 模型定义 98 | - **Remark** (str) - 备注 99 | - **RemoteVPNGatewayId** (str) - 对端网关Id 100 | - **RemoteVPNGatewayName** (str) - 对端网关名字 101 | - **Tag** (str) - 用户组 102 | - **VPCId** (str) - 所属VPCId 103 | - **VPCName** (str) - 所属VOC名字 104 | - **VPNGatewayId** (str) - 所属VPN网关id 105 | - **VPNGatewayName** (str) - VPN网关名字 106 | - **VPNTunnelId** (str) - 隧道id 107 | - **VPNTunnelName** (str) - 隧道名称 108 | 109 | """ 110 | d = {"ProjectId": self.config.project_id, "Region": self.config.region} 111 | req and d.update(req) 112 | d = apis.DescribeVPNTunnelRequestSchema().dumps(d) 113 | resp = self.invoke("DescribeVPNTunnel", d, **kwargs) 114 | return apis.DescribeVPNTunnelResponseSchema().loads(resp) 115 | -------------------------------------------------------------------------------- /tests/test_services/test_set_1202.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ Code is generated by ucloud-model, DO NOT EDIT IT. """ 4 | import pytest 5 | import logging 6 | from ucloud.core import exc 7 | from ucloud.testing import env, funcs, op, utest 8 | 9 | logger = logging.getLogger(__name__) 10 | scenario = utest.Scenario(1202) 11 | 12 | 13 | @pytest.mark.skipif(env.is_ut(), reason=env.get_skip_reason()) 14 | def test_set_1202(client, variables): 15 | scenario.initial(variables) 16 | scenario.variables["Protocol"] = "memcache" 17 | scenario.variables["ResourceType"] = "distributed" 18 | scenario.variables["Name"] = "distributed_memcache" 19 | scenario.run(client) 20 | 21 | 22 | @scenario.step( 23 | max_retries=0, 24 | retry_interval=0, 25 | startup_delay=0, 26 | fast_fail=False, 27 | validators=lambda variables: [("str_eq", "RetCode", 0)], 28 | action="CreateUMemSpace", 29 | ) 30 | def create_umem_space_00(client, variables): 31 | d = { 32 | "Zone": variables.get("Zone"), 33 | "Size": 8, 34 | "Region": variables.get("Region"), 35 | "Quantity": 1, 36 | "Protocol": variables.get("Protocol"), 37 | "Name": variables.get("Name"), 38 | "ChargeType": "Month", 39 | } 40 | try: 41 | resp = client.umem().create_umem_space(d) 42 | except exc.RetCodeException as e: 43 | resp = e.json() 44 | variables["Space_Id"] = utest.value_at_path(resp, "SpaceId") 45 | return resp 46 | 47 | 48 | @scenario.step( 49 | max_retries=30, 50 | retry_interval=10, 51 | startup_delay=3, 52 | fast_fail=False, 53 | validators=lambda variables: [ 54 | ("str_eq", "RetCode", 0), 55 | ("str_eq", "DataSet.0.State", "Running"), 56 | ], 57 | action="DescribeUMem", 58 | ) 59 | def describe_umem_01(client, variables): 60 | d = { 61 | "ResourceId": variables.get("Space_Id"), 62 | "Region": variables.get("Region"), 63 | "Protocol": variables.get("Protocol"), 64 | "Offset": 0, 65 | "Limit": 1000, 66 | } 67 | try: 68 | resp = client.invoke("DescribeUMem", d) 69 | except exc.RetCodeException as e: 70 | resp = e.json() 71 | return resp 72 | 73 | 74 | @scenario.step( 75 | max_retries=0, 76 | retry_interval=0, 77 | startup_delay=0, 78 | fast_fail=False, 79 | validators=lambda variables: [("str_eq", "RetCode", 0)], 80 | action="ResizeUMemSpace", 81 | ) 82 | def resize_umem_space_02(client, variables): 83 | d = { 84 | "Zone": variables.get("Zone"), 85 | "SpaceId": variables.get("Space_Id"), 86 | "Size": 14, 87 | "Region": variables.get("Region"), 88 | } 89 | try: 90 | resp = client.umem().resize_umem_space(d) 91 | except exc.RetCodeException as e: 92 | resp = e.json() 93 | return resp 94 | 95 | 96 | @scenario.step( 97 | max_retries=30, 98 | retry_interval=10, 99 | startup_delay=3, 100 | fast_fail=False, 101 | validators=lambda variables: [ 102 | ("str_eq", "RetCode", 0), 103 | ("str_eq", "DataSet.0.State", "Running"), 104 | ], 105 | action="DescribeUMem", 106 | ) 107 | def describe_umem_03(client, variables): 108 | d = { 109 | "ResourceId": variables.get("Space_Id"), 110 | "Region": variables.get("Region"), 111 | "Protocol": variables.get("Protocol"), 112 | "Offset": 0, 113 | "Limit": 1000, 114 | } 115 | try: 116 | resp = client.invoke("DescribeUMem", d) 117 | except exc.RetCodeException as e: 118 | resp = e.json() 119 | return resp 120 | 121 | 122 | @scenario.step( 123 | max_retries=0, 124 | retry_interval=0, 125 | startup_delay=0, 126 | fast_fail=False, 127 | validators=lambda variables: [("str_eq", "RetCode", 0)], 128 | action="DeleteUMem", 129 | ) 130 | def delete_umem_04(client, variables): 131 | d = { 132 | "Zone": variables.get("Zone"), 133 | "ResourceType": variables.get("ResourceType"), 134 | "ResourceId": variables.get("Space_Id"), 135 | "Region": variables.get("Region"), 136 | } 137 | try: 138 | resp = client.invoke("DeleteUMem", d) 139 | except exc.RetCodeException as e: 140 | resp = e.json() 141 | return resp 142 | 143 | 144 | @scenario.step( 145 | max_retries=3, 146 | retry_interval=1, 147 | startup_delay=0, 148 | fast_fail=False, 149 | validators=lambda variables: [ 150 | ("str_eq", "RetCode", 0), 151 | ("str_eq", "Action", "DescribeUMemSpaceResponse"), 152 | ("object_not_contains", "DataSet", variables.get("Space_Id")), 153 | ], 154 | action="DescribeUMemSpace", 155 | ) 156 | def describe_umem_space_05(client, variables): 157 | d = { 158 | "Zone": variables.get("Zone"), 159 | "SpaceId": variables.get("Space_Id"), 160 | "Region": variables.get("Region"), 161 | "Offset": 0, 162 | "Limit": 100, 163 | } 164 | try: 165 | resp = client.umem().describe_umem_space(d) 166 | except exc.RetCodeException as e: 167 | resp = e.json() 168 | return resp 169 | -------------------------------------------------------------------------------- /ucloud/testing/op.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import re 4 | from ucloud.testing.exc import CompareError 5 | 6 | 7 | def eq(value, expected): 8 | """ value is equal to expected 9 | """ 10 | assert value == expected 11 | 12 | 13 | def ne(value, expected): 14 | """ value is equal to expected 15 | """ 16 | assert value != expected 17 | 18 | 19 | def gt(value, expected): 20 | """ value is greater than expected 21 | """ 22 | assert float(value) > float(expected) 23 | 24 | 25 | def ge(value, expected): 26 | """ value is greater than or equal to expected 27 | """ 28 | assert float(value) >= float(expected) 29 | 30 | 31 | def abs_eq(value, expected): 32 | """ value is approx equal to expected 33 | """ 34 | assert round(float(value), 2) == round(float(expected), 2) 35 | 36 | 37 | def lt(value, expected): 38 | """ value is less than excepted 39 | """ 40 | assert float(value) < float(expected) 41 | 42 | 43 | def le(value, expected): 44 | """ value is less than or equal to excepted 45 | """ 46 | assert float(value) <= float(expected) 47 | 48 | 49 | def str_eq(value, expected): 50 | """ value is equal to excepted as string 51 | """ 52 | assert str(value) == str(expected) 53 | 54 | 55 | def float_eq(value, expected): 56 | """ value is equal to excepted as float 57 | """ 58 | assert round(float(value), 2) == round(float(expected), 2) 59 | 60 | 61 | def len_eq(value, expected): 62 | """ length of value is equal to excepted 63 | """ 64 | assert isinstance(expected, int) 65 | assert len(value) == expected 66 | 67 | 68 | def len_gt(value, expected): 69 | """ length of value is greater than excepted 70 | """ 71 | assert isinstance(expected, int) 72 | assert len(value) > expected 73 | 74 | 75 | def len_ge(value, expected): 76 | """ length of value is greater than or equal to excepted 77 | """ 78 | assert isinstance(expected, int) 79 | assert len(value) >= expected 80 | 81 | 82 | def len_lt(value, expected): 83 | """ length of value is less than excepted 84 | """ 85 | assert isinstance(expected, int) 86 | assert len(value) < expected 87 | 88 | 89 | def len_le(value, expected): 90 | """ length of value is less than or equal to excepted 91 | """ 92 | assert isinstance(expected, int) 93 | assert len(value) <= expected 94 | 95 | 96 | def contains(value, expected): 97 | """ value is contains expected 98 | """ 99 | assert expected in value 100 | 101 | 102 | def contained_by(value, expected): 103 | """ value is contained by expected 104 | """ 105 | assert value in expected 106 | 107 | 108 | def type_eq(value, expected): 109 | assert isinstance(value, expected) 110 | 111 | 112 | def regex(value, expected): 113 | assert isinstance(expected, str) 114 | assert isinstance(value, str) 115 | assert re.match(expected, value) 116 | 117 | 118 | def startswith(value, expected): 119 | assert str(value).startswith(expected) 120 | 121 | 122 | def endswith(value, expected): 123 | assert str(value).endswith(expected) 124 | 125 | 126 | def object_contains(value, expected): 127 | assert str(expected) in str(value) 128 | 129 | 130 | def object_not_contains(value, expected): 131 | assert str(expected) not in str(value) 132 | 133 | 134 | mapper = { 135 | "eq": eq, 136 | "equals": eq, 137 | "==": eq, 138 | "abs_eq": abs_eq, 139 | "abs_equals": abs_eq, 140 | "lt": lt, 141 | "less_than": lt, 142 | "le": le, 143 | "less_than_or_equals": le, 144 | "gt": gt, 145 | "greater_than": gt, 146 | "ge": ge, 147 | "greater_than_or_equals": ge, 148 | "ne": ne, 149 | "not_equals": ne, 150 | "str_eq": str_eq, 151 | "string_equals": str_eq, 152 | "float_eq": float_eq, 153 | "float_equals": float_eq, 154 | "len_eq": len_eq, 155 | "length_equals": len_eq, 156 | "count_eq": len_eq, 157 | "len_gt": len_gt, 158 | "count_gt": len_gt, 159 | "length_greater_than": len_gt, 160 | "count_greater_than": len_gt, 161 | "len_ge": len_ge, 162 | "count_ge": len_ge, 163 | "length_greater_than_or_equals": len_ge, 164 | "count_greater_than_or_equals": len_ge, 165 | "len_lt": len_lt, 166 | "count_lt": len_lt, 167 | "length_less_than": len_lt, 168 | "count_less_than": len_lt, 169 | "len_le": len_le, 170 | "count_le": len_le, 171 | "length_less_than_or_equals": len_le, 172 | "count_less_than_or_equals": len_le, 173 | "contains": contains, 174 | "contained_by": contained_by, 175 | "type": type_eq, 176 | "regex": regex, 177 | "startswith": startswith, 178 | "endswith": endswith, 179 | "object_contains": object_contains, 180 | "object_not_contains": object_not_contains, 181 | } 182 | 183 | 184 | def check(name, value, expected): 185 | if name not in mapper: 186 | raise CompareError("comparator {} is not found".format(name)) 187 | try: 188 | return mapper.get(name)(value, expected) 189 | except AssertionError as e: 190 | msg = "assert error, expect {} {} {}, got error {}".format( 191 | value, name, expected, e 192 | ) 193 | raise CompareError(msg) 194 | -------------------------------------------------------------------------------- /ucloud/services/pathx/schemas/apis.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ Code is generated by ucloud-model, DO NOT EDIT IT. """ 4 | from ucloud.core.typesystem import schema, fields 5 | from ucloud.services.pathx.schemas import models 6 | 7 | """ PathX API Schema 8 | """ 9 | """ 10 | API: CreateGlobalSSHInstance 11 | 12 | 创建GlobalSSH实例 13 | """ 14 | 15 | 16 | class CreateGlobalSSHInstanceRequestSchema(schema.RequestSchema): 17 | """ CreateGlobalSSHInstance - 创建GlobalSSH实例 18 | """ 19 | 20 | fields = { 21 | "Area": fields.Str(required=True, dump_to="Area"), 22 | "AreaCode": fields.Str(required=True, dump_to="AreaCode"), 23 | "ChargeType": fields.Str(required=False, dump_to="ChargeType"), 24 | "CouponId": fields.Str(required=False, dump_to="CouponId"), 25 | "Port": fields.Int(required=True, dump_to="Port"), 26 | "ProjectId": fields.Str(required=True, dump_to="ProjectId"), 27 | "Quantity": fields.Int(required=False, dump_to="Quantity"), 28 | "Remark": fields.Str(required=False, dump_to="Remark"), 29 | "TargetIP": fields.Str(required=True, dump_to="TargetIP"), 30 | } 31 | 32 | 33 | class CreateGlobalSSHInstanceResponseSchema(schema.ResponseSchema): 34 | """ CreateGlobalSSHInstance - 创建GlobalSSH实例 35 | """ 36 | 37 | fields = { 38 | "AcceleratingDomain": fields.Str( 39 | required=False, load_from="AcceleratingDomain" 40 | ), 41 | "InstanceId": fields.Str(required=True, load_from="InstanceId"), 42 | "Message": fields.Str(required=False, load_from="Message"), 43 | } 44 | 45 | 46 | """ 47 | API: DeleteGlobalSSHInstance 48 | 49 | 删除GlobalSSH实例 50 | """ 51 | 52 | 53 | class DeleteGlobalSSHInstanceRequestSchema(schema.RequestSchema): 54 | """ DeleteGlobalSSHInstance - 删除GlobalSSH实例 55 | """ 56 | 57 | fields = { 58 | "InstanceId": fields.Str(required=True, dump_to="InstanceId"), 59 | "ProjectId": fields.Str(required=True, dump_to="ProjectId"), 60 | } 61 | 62 | 63 | class DeleteGlobalSSHInstanceResponseSchema(schema.ResponseSchema): 64 | """ DeleteGlobalSSHInstance - 删除GlobalSSH实例 65 | """ 66 | 67 | fields = {"Message": fields.Str(required=False, load_from="Message")} 68 | 69 | 70 | """ 71 | API: DescribeGlobalSSHArea 72 | 73 | 获取GlobalSSH覆盖的地区列表 用于控制显示哪些机房地域可以使用SSH特性 74 | """ 75 | 76 | 77 | class DescribeGlobalSSHAreaRequestSchema(schema.RequestSchema): 78 | """ DescribeGlobalSSHArea - 获取GlobalSSH覆盖的地区列表 用于控制显示哪些机房地域可以使用SSH特性 79 | """ 80 | 81 | fields = { 82 | "ProjectId": fields.Str(required=False, dump_to="ProjectId"), 83 | "Region": fields.Str(required=False, dump_to="Region"), 84 | } 85 | 86 | 87 | class DescribeGlobalSSHAreaResponseSchema(schema.ResponseSchema): 88 | """ DescribeGlobalSSHArea - 获取GlobalSSH覆盖的地区列表 用于控制显示哪些机房地域可以使用SSH特性 89 | """ 90 | 91 | fields = { 92 | "AreaSet": fields.List( 93 | models.GlobalSSHAreaSchema(), required=False, load_from="AreaSet" 94 | ), 95 | "Message": fields.Str(required=False, load_from="Message"), 96 | } 97 | 98 | 99 | """ 100 | API: DescribeGlobalSSHInstance 101 | 102 | 获取GlobalSSH实例列表(传实例ID获取单个实例信息,不传获取项目下全部实例) 103 | """ 104 | 105 | 106 | class DescribeGlobalSSHInstanceRequestSchema(schema.RequestSchema): 107 | """ DescribeGlobalSSHInstance - 获取GlobalSSH实例列表(传实例ID获取单个实例信息,不传获取项目下全部实例) 108 | """ 109 | 110 | fields = { 111 | "InstanceId": fields.Str(required=False, dump_to="InstanceId"), 112 | "ProjectId": fields.Str(required=True, dump_to="ProjectId"), 113 | } 114 | 115 | 116 | class DescribeGlobalSSHInstanceResponseSchema(schema.ResponseSchema): 117 | """ DescribeGlobalSSHInstance - 获取GlobalSSH实例列表(传实例ID获取单个实例信息,不传获取项目下全部实例) 118 | """ 119 | 120 | fields = { 121 | "InstanceSet": fields.List( 122 | models.GlobalSSHInfoSchema(), 123 | required=False, 124 | load_from="InstanceSet", 125 | ) 126 | } 127 | 128 | 129 | """ 130 | API: ModifyGlobalSSHPort 131 | 132 | 修改GlobalSSH端口 133 | """ 134 | 135 | 136 | class ModifyGlobalSSHPortRequestSchema(schema.RequestSchema): 137 | """ ModifyGlobalSSHPort - 修改GlobalSSH端口 138 | """ 139 | 140 | fields = { 141 | "InstanceId": fields.Str(required=True, dump_to="InstanceId"), 142 | "Port": fields.Int(required=True, dump_to="Port"), 143 | "ProjectId": fields.Str(required=True, dump_to="ProjectId"), 144 | } 145 | 146 | 147 | class ModifyGlobalSSHPortResponseSchema(schema.ResponseSchema): 148 | """ ModifyGlobalSSHPort - 修改GlobalSSH端口 149 | """ 150 | 151 | fields = {"Message": fields.Str(required=False, load_from="Message")} 152 | 153 | 154 | """ 155 | API: ModifyGlobalSSHRemark 156 | 157 | 修改GlobalSSH备注 158 | """ 159 | 160 | 161 | class ModifyGlobalSSHRemarkRequestSchema(schema.RequestSchema): 162 | """ ModifyGlobalSSHRemark - 修改GlobalSSH备注 163 | """ 164 | 165 | fields = { 166 | "InstanceId": fields.Str(required=True, dump_to="InstanceId"), 167 | "ProjectId": fields.Str(required=True, dump_to="ProjectId"), 168 | "Remark": fields.Str(required=False, dump_to="Remark"), 169 | } 170 | 171 | 172 | class ModifyGlobalSSHRemarkResponseSchema(schema.ResponseSchema): 173 | """ ModifyGlobalSSHRemark - 修改GlobalSSH备注 174 | """ 175 | 176 | fields = {"Message": fields.Str(required=False, load_from="Message")} 177 | -------------------------------------------------------------------------------- /ucloud/services/vpc/schemas/models.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ Code is generated by ucloud-model, DO NOT EDIT IT. """ 4 | from ucloud.core.typesystem import schema, fields 5 | 6 | 7 | class RouteRuleInfoSchema(schema.ResponseSchema): 8 | """ RouteRuleInfo - 路由规则信息 9 | """ 10 | 11 | fields = { 12 | "DstAddr": fields.Str(required=False, load_from="DstAddr"), 13 | "NexthopId": fields.Str(required=False, load_from="NexthopId"), 14 | "NexthopType": fields.Str(required=False, load_from="NexthopType"), 15 | "Remark": fields.Str(required=False, load_from="Remark"), 16 | "RouteRuleId": fields.Str(required=False, load_from="RouteRuleId"), 17 | "RuleType": fields.Int(required=False, load_from="RuleType"), 18 | } 19 | 20 | 21 | class RouteTableInfoSchema(schema.ResponseSchema): 22 | """ RouteTableInfo - 路由表信息 23 | """ 24 | 25 | fields = { 26 | "CreateTime": fields.Int(required=False, load_from="CreateTime"), 27 | "Remark": fields.Str(required=False, load_from="Remark"), 28 | "RouteRules": fields.List(RouteRuleInfoSchema()), 29 | "RouteTableId": fields.Str(required=False, load_from="RouteTableId"), 30 | "RouteTableType": fields.Int( 31 | required=False, load_from="RouteTableType" 32 | ), 33 | "SubnetCount": fields.Str(required=False, load_from="SubnetCount"), 34 | "Tag": fields.Str(required=False, load_from="Tag"), 35 | "VPCId": fields.Str(required=False, load_from="VPCId"), 36 | "VPCName": fields.Str(required=False, load_from="VPCName"), 37 | } 38 | 39 | 40 | class SubnetInfoSchema(schema.ResponseSchema): 41 | """ SubnetInfo - 子网信息 42 | """ 43 | 44 | fields = { 45 | "AvailableIPs": fields.Int(required=False, load_from="AvailableIPs"), 46 | "CreateTime": fields.Int(required=False, load_from="CreateTime"), 47 | "Gateway": fields.Str(required=False, load_from="Gateway"), 48 | "HasNATGW": fields.Bool(required=False, load_from="HasNATGW"), 49 | "IPv6Network": fields.Str(required=False, load_from="IPv6Network"), 50 | "Netmask": fields.Str(required=False, load_from="Netmask"), 51 | "Remark": fields.Str(required=False, load_from="Remark"), 52 | "RouteTableId": fields.Str(required=False, load_from="RouteTableId"), 53 | "Subnet": fields.Str(required=False, load_from="Subnet"), 54 | "SubnetId": fields.Str(required=False, load_from="SubnetId"), 55 | "SubnetName": fields.Str(required=False, load_from="SubnetName"), 56 | "SubnetType": fields.Int(required=False, load_from="SubnetType"), 57 | "Tag": fields.Str(required=False, load_from="Tag"), 58 | "VPCId": fields.Str(required=False, load_from="VPCId"), 59 | "VPCName": fields.Str(required=False, load_from="VPCName"), 60 | "Zone": fields.Str(required=False, load_from="Zone"), 61 | } 62 | 63 | 64 | class SubnetResourceSchema(schema.ResponseSchema): 65 | """ SubnetResource - 子网下资源 66 | """ 67 | 68 | fields = { 69 | "IP": fields.Str(required=False, load_from="IP"), 70 | "IPv6Address": fields.Str(required=False, load_from="IPv6Address"), 71 | "Name": fields.Str(required=False, load_from="Name"), 72 | "ResourceId": fields.Str(required=False, load_from="ResourceId"), 73 | "ResourceType": fields.Str(required=False, load_from="ResourceType"), 74 | "SubResourceId": fields.Str(required=False, load_from="SubResourceId"), 75 | "SubResourceName": fields.Str( 76 | required=False, load_from="SubResourceName" 77 | ), 78 | "SubResourceType": fields.Str( 79 | required=False, load_from="SubResourceType" 80 | ), 81 | } 82 | 83 | 84 | class VPCNetworkInfoSchema(schema.ResponseSchema): 85 | """ VPCNetworkInfo - vpc地址空间信息 86 | """ 87 | 88 | fields = { 89 | "Network": fields.Str(required=False, load_from="Network"), 90 | "SubnetCount": fields.Int(required=False, load_from="SubnetCount"), 91 | } 92 | 93 | 94 | class VPCInfoSchema(schema.ResponseSchema): 95 | """ VPCInfo - VPC信息 96 | """ 97 | 98 | fields = { 99 | "CreateTime": fields.Int(required=True, load_from="CreateTime"), 100 | "IPv6Network": fields.Str(required=False, load_from="IPv6Network"), 101 | "Name": fields.Str(required=True, load_from="Name"), 102 | "Network": fields.List(fields.Str()), 103 | "NetworkInfo": fields.List(VPCNetworkInfoSchema()), 104 | "OperatorName": fields.Str(required=False, load_from="OperatorName"), 105 | "SubnetCount": fields.Int(required=True, load_from="SubnetCount"), 106 | "Tag": fields.Str(required=True, load_from="Tag"), 107 | "UpdateTime": fields.Int(required=True, load_from="UpdateTime"), 108 | "VPCId": fields.Str(required=False, load_from="VPCId"), 109 | } 110 | 111 | 112 | class VPCIntercomInfoSchema(schema.ResponseSchema): 113 | """ VPCIntercomInfo - 114 | """ 115 | 116 | fields = { 117 | "DstRegion": fields.Str(required=False, load_from="DstRegion"), 118 | "Name": fields.Str(required=False, load_from="Name"), 119 | "Network": fields.List(fields.Str()), 120 | "ProjectId": fields.Str(required=False, load_from="ProjectId"), 121 | "Tag": fields.Str(required=False, load_from="Tag"), 122 | "VPCId": fields.Str(required=False, load_from="VPCId"), 123 | } 124 | -------------------------------------------------------------------------------- /ucloud/core/client/_client.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import logging 4 | import sys 5 | from ucloud import version 6 | from ucloud.core.client._cfg import Config 7 | from ucloud.core.transport import ( 8 | Transport, 9 | RequestsTransport, 10 | Request, 11 | SSLOption, 12 | ) 13 | from ucloud.core.typesystem import encoder 14 | from ucloud.core.utils import log 15 | from ucloud.core.utils.middleware import Middleware 16 | from ucloud.core import auth, exc 17 | 18 | default_transport = RequestsTransport() 19 | 20 | 21 | class Client(object): 22 | def __init__(self, config, transport=None, middleware=None, logger=None): 23 | cfg, cred = self._parse_dict_config(config) 24 | self.config = cfg 25 | self.credential = cred 26 | self.transport = transport or default_transport 27 | self.logger = logger or log.default_logger 28 | if middleware is None: 29 | middleware = Middleware() 30 | middleware.response(self.logged_response_handler) 31 | middleware.request(self.logged_request_handler) 32 | self._middleware = middleware 33 | 34 | def invoke(self, action, args=None, **options): 35 | """ invoke will invoke the action with arguments data and options 36 | 37 | :param str action: the api action, like `CreateUHostInstance` 38 | :param dict args: arguments of api(action), see doc: `UCloud API Documentation `__ 39 | :return: 40 | """ 41 | retries = 0 42 | max_retries = options.get("max_retries") or self.config.max_retries 43 | while retries <= max_retries: 44 | try: 45 | return self._send(action, args or {}, **options) 46 | except exc.UCloudException as e: 47 | for handler in self.middleware.exception_handlers: 48 | handler(e) 49 | if e.retryable and retries != max_retries: 50 | logging.info( 51 | "Retrying {action}: {args}".format( 52 | action=action, args=args 53 | ) 54 | ) 55 | retries += 1 56 | continue 57 | raise e 58 | except Exception as e: 59 | for handler in self.middleware.exception_handlers: 60 | handler(e) 61 | raise e 62 | 63 | @property 64 | def middleware(self): 65 | return self._middleware 66 | 67 | def logged_request_handler(self, req): 68 | self.logger.info("[request] {} {}".format(req.get("Action", ""), req)) 69 | return req 70 | 71 | def logged_response_handler(self, resp): 72 | self.logger.info( 73 | "[response] {} {}".format(resp.get("Action", ""), resp) 74 | ) 75 | return resp 76 | 77 | @staticmethod 78 | def _parse_dict_config(config): 79 | return Config.from_dict(config), auth.Credential.from_dict(config) 80 | 81 | def _send(self, action, args, **options): 82 | args["Action"] = action 83 | for handler in self.middleware.request_handlers: 84 | args = handler(args) 85 | req = self._build_http_request(args) 86 | max_retries = options.get("max_retries") or self.config.max_retries 87 | timeout = options.get("timeout") or self.config.timeout 88 | resp = self.transport.send( 89 | req, 90 | ssl_option=SSLOption( 91 | self.config.ssl_verify, 92 | self.config.ssl_cacert, 93 | self.config.ssl_cert, 94 | self.config.ssl_key, 95 | ), 96 | timeout=timeout, 97 | max_retries=max_retries, 98 | ).json() 99 | for handler in self.middleware.response_handlers: 100 | resp = handler(resp) 101 | if int(resp.get("RetCode", -1)) != 0: 102 | raise exc.RetCodeException( 103 | action=req.data.get("Action"), 104 | code=int(resp.get("RetCode")), 105 | message=resp.get("Message"), 106 | ) 107 | return resp 108 | 109 | def _build_http_request(self, args): 110 | config = { 111 | "Region": self.config.region, 112 | "ProjectId": self.config.project_id, 113 | } 114 | payload = {k: v for k, v in config.items() if v is not None} 115 | payload.update({k: v for k, v in args.items() if v is not None}) 116 | payload = encoder.encode(payload) 117 | payload["Signature"] = self.credential.verify_ac(payload) 118 | return Request( 119 | url=self.config.base_url, 120 | method="post", 121 | data=payload, 122 | headers={ 123 | "User-Agent": self._build_user_agent(), 124 | "Content-Type": "application/x-www-form-urlencoded", 125 | }, 126 | ) 127 | 128 | def _build_user_agent(self): 129 | python_version = "{v[0]}.{v[1]}.{v[2]}".format(v=sys.version_info) 130 | user_agent = "Python/{python_version} Python-SDK/{sdk_version}".format( 131 | python_version=python_version, sdk_version=version.version 132 | ) + (self.config.user_agent or "") 133 | return user_agent 134 | 135 | def __repr__(self): 136 | return '<{}(region="{}")>'.format( 137 | self.__class__.__name__, self.config.region 138 | ) 139 | -------------------------------------------------------------------------------- /ucloud/services/uaccount/client.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ Code is generated by ucloud-model, DO NOT EDIT IT. """ 4 | from ucloud.core.client import Client 5 | from ucloud.services.uaccount.schemas import apis 6 | 7 | 8 | class UAccountClient(Client): 9 | def __init__(self, config, transport=None, middleware=None, logger=None): 10 | super(UAccountClient, self).__init__( 11 | config, transport, middleware, logger 12 | ) 13 | 14 | def create_project(self, req=None, **kwargs): 15 | """ CreateProject - 创建项目 16 | 17 | **Request** 18 | 19 | - **ProjectName** (str) - (Required) 项目名称 20 | 21 | **Response** 22 | 23 | - **ProjectId** (str) - 所创建项目的Id 24 | 25 | """ 26 | d = {} 27 | req and d.update(req) 28 | d = apis.CreateProjectRequestSchema().dumps(d) 29 | kwargs["max_retries"] = 0 30 | resp = self.invoke("CreateProject", d, **kwargs) 31 | return apis.CreateProjectResponseSchema().loads(resp) 32 | 33 | def get_project_list(self, req=None, **kwargs): 34 | """ GetProjectList - 获取项目列表 35 | 36 | **Request** 37 | 38 | - **IsFinance** (str) - 是否是财务账号 39 | 40 | **Response** 41 | 42 | - **ProjectCount** (int) - 项目总数 43 | - **ProjectSet** (list) - 见 **ProjectListInfo** 模型定义 44 | 45 | **Response Model** 46 | 47 | **ProjectListInfo** 48 | 49 | - **CreateTime** (int) - 创建时间(Unix时间戳) 50 | - **IsDefault** (bool) - 是否为默认项目 51 | - **MemberCount** (int) - 项目下成员数量 52 | - **ParentId** (str) - 父项目ID 53 | - **ParentName** (str) - 父项目名称 54 | - **ProjectId** (str) - 项目ID 55 | - **ProjectName** (str) - 项目名称 56 | - **ResourceCount** (int) - 项目下资源数量 57 | 58 | """ 59 | d = {} 60 | req and d.update(req) 61 | d = apis.GetProjectListRequestSchema().dumps(d) 62 | resp = self.invoke("GetProjectList", d, **kwargs) 63 | return apis.GetProjectListResponseSchema().loads(resp) 64 | 65 | def get_region(self, req=None, **kwargs): 66 | """ GetRegion - 获取用户在各数据中心的权限等信息 67 | 68 | **Request** 69 | 70 | 71 | **Response** 72 | 73 | - **Regions** (list) - 见 **RegionInfo** 模型定义 74 | 75 | **Response Model** 76 | 77 | **RegionInfo** 78 | 79 | - **BitMaps** (str) - 用户在此数据中心的权限位 80 | - **IsDefault** (bool) - 是否用户当前默认数据中心 81 | - **Region** (str) - 地域名字,如cn-bj 82 | - **RegionId** (int) - 数据中心ID 83 | - **RegionName** (str) - 数据中心名称 84 | - **Zone** (str) - 可用区名字,如cn-bj-01 85 | 86 | """ 87 | d = {} 88 | req and d.update(req) 89 | d = apis.GetRegionRequestSchema().dumps(d) 90 | resp = self.invoke("GetRegion", d, **kwargs) 91 | return apis.GetRegionResponseSchema().loads(resp) 92 | 93 | def get_user_info(self, req=None, **kwargs): 94 | """ GetUserInfo - 获取用户信息 95 | 96 | **Request** 97 | 98 | 99 | **Response** 100 | 101 | - **DataSet** (list) - 见 **UserInfo** 模型定义 102 | 103 | **Response Model** 104 | 105 | **UserInfo** 106 | 107 | - **Admin** (int) - 是否超级管理员 0:否 1:是 108 | - **Administrator** (str) - 管理员 109 | - **AuthState** (str) - 实名认证状态 110 | - **City** (str) - 城市 111 | - **CompanyName** (str) - 公司名称 112 | - **Finance** (int) - 是否有财务权限 0:否 1:是 113 | - **IndustryType** (int) - 所属行业 114 | - **PhonePrefix** (str) - 国际号码前缀 115 | - **Province** (str) - 省份 116 | - **UserAddress** (str) - 公司地址 117 | - **UserEmail** (str) - 用户邮箱 118 | - **UserId** (int) - 用户Id 119 | - **UserName** (str) - 称呼 120 | - **UserPhone** (str) - 用户手机 121 | - **UserType** (int) - 会员类型 122 | - **UserVersion** (int) - 是否子帐户(大于100为子帐户) 123 | 124 | """ 125 | d = {} 126 | req and d.update(req) 127 | d = apis.GetUserInfoRequestSchema().dumps(d) 128 | resp = self.invoke("GetUserInfo", d, **kwargs) 129 | return apis.GetUserInfoResponseSchema().loads(resp) 130 | 131 | def modify_project(self, req=None, **kwargs): 132 | """ ModifyProject - 修改项目 133 | 134 | **Request** 135 | 136 | - **ProjectId** (str) - (Config) 项目ID。不填写为默认项目,子帐号必须填写。 请参考 `GetProjectList接口 `_ 137 | - **ProjectName** (str) - (Required) 新的项目名称 138 | 139 | **Response** 140 | 141 | 142 | """ 143 | d = {"ProjectId": self.config.project_id} 144 | req and d.update(req) 145 | d = apis.ModifyProjectRequestSchema().dumps(d) 146 | resp = self.invoke("ModifyProject", d, **kwargs) 147 | return apis.ModifyProjectResponseSchema().loads(resp) 148 | 149 | def terminate_project(self, req=None, **kwargs): 150 | """ TerminateProject - 删除项目 151 | 152 | **Request** 153 | 154 | - **ProjectId** (str) - (Config) 项目ID,不填写为默认项目,子帐号必须填写。 155 | 156 | **Response** 157 | 158 | 159 | """ 160 | d = {"ProjectId": self.config.project_id} 161 | req and d.update(req) 162 | d = apis.TerminateProjectRequestSchema().dumps(d) 163 | resp = self.invoke("TerminateProject", d, **kwargs) 164 | return apis.TerminateProjectResponseSchema().loads(resp) 165 | -------------------------------------------------------------------------------- /ucloud/services/uhub/schemas/apis.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | """ Code is generated by ucloud-model, DO NOT EDIT IT. """ 4 | from ucloud.core.typesystem import schema, fields 5 | from ucloud.services.uhub.schemas import models 6 | 7 | """ UHub API Schema 8 | """ 9 | """ 10 | API: CreateRepo 11 | 12 | 创建镜像仓库 13 | """ 14 | 15 | 16 | class CreateRepoRequestSchema(schema.RequestSchema): 17 | """ CreateRepo - 创建镜像仓库 18 | """ 19 | 20 | fields = { 21 | "Description": fields.Str(required=False, dump_to="Description"), 22 | "IsShared": fields.Bool(required=False, dump_to="IsShared"), 23 | "ProjectId": fields.Str(required=False, dump_to="ProjectId"), 24 | "RepoName": fields.Str(required=True, dump_to="RepoName"), 25 | } 26 | 27 | 28 | class CreateRepoResponseSchema(schema.ResponseSchema): 29 | """ CreateRepo - 创建镜像仓库 30 | """ 31 | 32 | fields = {"Message": fields.Str(required=False, load_from="Message")} 33 | 34 | 35 | """ 36 | API: DeleteRepo 37 | 38 | 删除镜像仓库 39 | """ 40 | 41 | 42 | class DeleteRepoRequestSchema(schema.RequestSchema): 43 | """ DeleteRepo - 删除镜像仓库 44 | """ 45 | 46 | fields = { 47 | "ProjectId": fields.Str(required=False, dump_to="ProjectId"), 48 | "RepoName": fields.Str(required=True, dump_to="RepoName"), 49 | } 50 | 51 | 52 | class DeleteRepoResponseSchema(schema.ResponseSchema): 53 | """ DeleteRepo - 删除镜像仓库 54 | """ 55 | 56 | fields = {} 57 | 58 | 59 | """ 60 | API: DeleteRepoImage 61 | 62 | 删除镜像 63 | """ 64 | 65 | 66 | class DeleteRepoImageRequestSchema(schema.RequestSchema): 67 | """ DeleteRepoImage - 删除镜像 68 | """ 69 | 70 | fields = { 71 | "ImageName": fields.Str(required=True, dump_to="ImageName"), 72 | "ProjectId": fields.Str(required=False, dump_to="ProjectId"), 73 | "RepoName": fields.Str(required=True, dump_to="RepoName"), 74 | "TagName": fields.Str(required=False, dump_to="TagName"), 75 | } 76 | 77 | 78 | class DeleteRepoImageResponseSchema(schema.ResponseSchema): 79 | """ DeleteRepoImage - 删除镜像 80 | """ 81 | 82 | fields = {} 83 | 84 | 85 | """ 86 | API: GetImageTag 87 | 88 | 获取镜像tag 89 | """ 90 | 91 | 92 | class GetImageTagRequestSchema(schema.RequestSchema): 93 | """ GetImageTag - 获取镜像tag 94 | """ 95 | 96 | fields = { 97 | "ImageName": fields.Str(required=True, dump_to="ImageName"), 98 | "Limit": fields.Int(required=False, dump_to="Limit"), 99 | "Offset": fields.Int(required=False, dump_to="Offset"), 100 | "ProjectId": fields.Str(required=False, dump_to="ProjectId"), 101 | "RepoName": fields.Str(required=True, dump_to="RepoName"), 102 | "TagName": fields.Str(required=False, dump_to="TagName"), 103 | } 104 | 105 | 106 | class GetImageTagResponseSchema(schema.ResponseSchema): 107 | """ GetImageTag - 获取镜像tag 108 | """ 109 | 110 | fields = { 111 | "TagSet": fields.List( 112 | models.TagSetSchema(), required=True, load_from="TagSet" 113 | ), 114 | "TotalCount": fields.Int(required=True, load_from="TotalCount"), 115 | } 116 | 117 | 118 | """ 119 | API: GetRepo 120 | 121 | 获取镜像仓库 122 | """ 123 | 124 | 125 | class GetRepoRequestSchema(schema.RequestSchema): 126 | """ GetRepo - 获取镜像仓库 127 | """ 128 | 129 | fields = { 130 | "Limit": fields.Int(required=False, dump_to="Limit"), 131 | "Offset": fields.Int(required=False, dump_to="Offset"), 132 | "ProjectId": fields.Str(required=False, dump_to="ProjectId"), 133 | "Type": fields.Str(required=False, dump_to="Type"), 134 | } 135 | 136 | 137 | class GetRepoResponseSchema(schema.ResponseSchema): 138 | """ GetRepo - 获取镜像仓库 139 | """ 140 | 141 | fields = { 142 | "RepoSet": fields.List( 143 | models.RepoSetSchema(), required=True, load_from="RepoSet" 144 | ), 145 | "TotalCount": fields.Int(required=True, load_from="TotalCount"), 146 | } 147 | 148 | 149 | """ 150 | API: GetRepoImage 151 | 152 | 获取镜像仓库下的镜像 153 | """ 154 | 155 | 156 | class GetRepoImageRequestSchema(schema.RequestSchema): 157 | """ GetRepoImage - 获取镜像仓库下的镜像 158 | """ 159 | 160 | fields = { 161 | "Limit": fields.Int(required=False, dump_to="Limit"), 162 | "Offset": fields.Int(required=False, dump_to="Offset"), 163 | "ProjectId": fields.Str(required=False, dump_to="ProjectId"), 164 | "RepoName": fields.Str(required=True, dump_to="RepoName"), 165 | } 166 | 167 | 168 | class GetRepoImageResponseSchema(schema.ResponseSchema): 169 | """ GetRepoImage - 获取镜像仓库下的镜像 170 | """ 171 | 172 | fields = { 173 | "ImageSet": fields.List( 174 | models.ImageSetSchema(), required=True, load_from="ImageSet" 175 | ), 176 | "TotalCount": fields.Int(required=True, load_from="TotalCount"), 177 | } 178 | 179 | 180 | """ 181 | API: UpdateRepo 182 | 183 | 更新镜像仓库 184 | """ 185 | 186 | 187 | class UpdateRepoRequestSchema(schema.RequestSchema): 188 | """ UpdateRepo - 更新镜像仓库 189 | """ 190 | 191 | fields = { 192 | "Description": fields.Str(required=False, dump_to="Description"), 193 | "IsShared": fields.Str(required=False, dump_to="IsShared"), 194 | "ProjectId": fields.Str(required=False, dump_to="ProjectId"), 195 | "RepoName": fields.Str(required=True, dump_to="RepoName"), 196 | } 197 | 198 | 199 | class UpdateRepoResponseSchema(schema.ResponseSchema): 200 | """ UpdateRepo - 更新镜像仓库 201 | """ 202 | 203 | fields = {"Message": fields.Str(required=False, load_from="Message")} 204 | --------------------------------------------------------------------------------