diff --git a/conf/cloud.profiles b/conf/cloud.profiles index 0d02d5d2c4f8..4a77fd73361e 100644 --- a/conf/cloud.profiles +++ b/conf/cloud.profiles @@ -25,3 +25,14 @@ # script: Fedora # minion: # cheese: edam + +#tencentcloud-guangzhou-s1sm1: +# provider: my-tencentcloud-config +# availability_zone: ap-guangzhou-3 +# image: img-31tjrtph +# size: S1.SMALL1 +# allocate_public_ip: True +# internet_max_bandwidth_out: 1 +# password: '153e41ec96140152' +# securitygroups: +# - sg-5e90804b diff --git a/conf/cloud.profiles.d/tencentcloud-guangzhou-s1sm1.profiles b/conf/cloud.profiles.d/tencentcloud-guangzhou-s1sm1.profiles new file mode 100644 index 000000000000..ab66f59f9807 --- /dev/null +++ b/conf/cloud.profiles.d/tencentcloud-guangzhou-s1sm1.profiles @@ -0,0 +1,10 @@ +#tencentcloud-guangzhou-s1sm1: +# provider: my-tencentcloud-config +# availability_zone: ap-guangzhou-3 +# image: img-31tjrtph +# size: S1.SMALL1 +# allocate_public_ip: True +# internet_max_bandwidth_out: 1 +# password: '153e41ec96140152' +# securitygroups: +# - sg-5e90804b diff --git a/conf/cloud.providers b/conf/cloud.providers index a8d890dc9c01..9031dbc673df 100644 --- a/conf/cloud.providers +++ b/conf/cloud.providers @@ -87,3 +87,12 @@ #my-scaleway-config: # driver: scaleway + +#my-tencentcloud-config: + # driver: tencentcloud + # TencentCloud Secret Id + # id: AKIDA64pOio9BMemkApzevX0HS169S4b750A + # TencentCloud Secret Key + # key: 8r2xmPn0C5FDvRAlmcJimiTZKVRsk260 + # TencentCloud Region + # location: ap-guangzhou diff --git a/conf/cloud.providers.d/tencent.conf b/conf/cloud.providers.d/tencent.conf new file mode 100644 index 000000000000..fadbdc9f8b09 --- /dev/null +++ b/conf/cloud.providers.d/tencent.conf @@ -0,0 +1,8 @@ +#my-tencentcloud-config: + # driver: tencentcloud + # TencentCloud Secret Id + # id: AKIDA64pOio9BMemkApzevX0HS169S4b750A + # TencentCloud Secret Key + # key: 8r2xmPn0C5FDvRAlmcJimiTZKVRsk260 + # TencentCloud Region + # location: ap-guangzhou diff --git a/doc/.tx/config b/doc/.tx/config index f766c4a9100e..24dbbb3fd41c 100644 --- a/doc/.tx/config +++ b/doc/.tx/config @@ -3092,6 +3092,12 @@ source_file = _build/locale/ref/clouds/all/salt.cloud.clouds.softlayer.pot source_lang = en source_name = ref/clouds/all/salt.cloud.clouds.softlayer.rst +[salt.ref--clouds--all--salt_cloud_clouds_tencentcloud] +file_filter = locale//LC_MESSAGES/ref/clouds/all/salt.cloud.clouds.tencentcloud.po +source_file = _build/locale/ref/clouds/all/salt.cloud.clouds.tencentcloud.pot +source_lang = en +source_name = ref/clouds/all/salt.cloud.clouds.tencentcloud.rst + [salt.topics--cloud--action] file_filter = locale//LC_MESSAGES/topics/cloud/action.po source_file = _build/locale/topics/cloud/action.pot @@ -3488,6 +3494,12 @@ source_file = _build/locale/topics/cloud/salt.pot source_lang = en source_name = topics/cloud/salt.rst +[salt.topics--cloud--tencentcloud] +file_filter = locale//LC_MESSAGES/topics/cloud/tencentcloud.po +source_file = _build/locale/topics/cloud/tencentcloud.pot +source_lang = en +source_name = topics/cloud/tencentcloud.rst + [salt.topics--cloud--troubleshooting] file_filter = locale//LC_MESSAGES/topics/cloud/troubleshooting.po source_file = _build/locale/topics/cloud/troubleshooting.pot diff --git a/doc/ref/clouds/all/index.rst b/doc/ref/clouds/all/index.rst index 4b3c76a33c16..d2a8bc6c7610 100644 --- a/doc/ref/clouds/all/index.rst +++ b/doc/ref/clouds/all/index.rst @@ -38,6 +38,7 @@ cloud modules scaleway softlayer softlayer_hw + tencentcloud vagrant virtualbox vmware diff --git a/doc/ref/clouds/all/salt.cloud.clouds.tencentcloud.rst b/doc/ref/clouds/all/salt.cloud.clouds.tencentcloud.rst new file mode 100644 index 000000000000..f259372f4d73 --- /dev/null +++ b/doc/ref/clouds/all/salt.cloud.clouds.tencentcloud.rst @@ -0,0 +1,6 @@ +============================== +salt.cloud.clouds.tencentcloud +============================== + +.. automodule:: salt.cloud.clouds.tencentcloud + :members: diff --git a/doc/spelling_wordlist.txt b/doc/spelling_wordlist.txt index c097ef1c5914..f8160ccccab2 100644 --- a/doc/spelling_wordlist.txt +++ b/doc/spelling_wordlist.txt @@ -815,6 +815,8 @@ tarball tbz tcp templating +tencent +tencentcloud testrb tgt tgz diff --git a/doc/topics/cloud/features.rst b/doc/topics/cloud/features.rst index f270198deedf..c43bd350b2fb 100644 --- a/doc/topics/cloud/features.rst +++ b/doc/topics/cloud/features.rst @@ -38,26 +38,26 @@ These are features that are available for almost every cloud host. .. container:: scrollable - +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+-------+-------+---------+---------+------+ - | |AWS |CloudStack|Digital|EC2|GoGrid|JoyEnt|Linode|OpenStack|Parallels|Rackspace|Saltify|Vagrant|Softlayer|Softlayer|Aliyun| - | |(Legacy)| |Ocean | | | | | | |(Legacy) | | | |Hardware | | - +=======================+========+==========+=======+===+======+======+======+=========+=========+=========+=======+=======+=========+=========+======+ - |Query |Yes |Yes |Yes |Yes|Yes |Yes |Yes |Yes |Yes |Yes |[1] |[1] |Yes |Yes |Yes | - +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+-------+-------+---------+---------+------+ - |Full Query |Yes |Yes |Yes |Yes|Yes |Yes |Yes |Yes |Yes |Yes |[1] |[1] |Yes |Yes |Yes | - +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+-------+-------+---------+---------+------+ - |Selective Query |Yes |Yes |Yes |Yes|Yes |Yes |Yes |Yes |Yes |Yes |[1] |[1] |Yes |Yes |Yes | - +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+-------+-------+---------+---------+------+ - |List Sizes |Yes |Yes |Yes |Yes|Yes |Yes |Yes |Yes |Yes |Yes |[2] |[2] |Yes |Yes |Yes | - +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+-------+-------+---------+---------+------+ - |List Images |Yes |Yes |Yes |Yes|Yes |Yes |Yes |Yes |Yes |Yes |Yes |Yes |Yes |Yes |Yes | - +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+-------+-------+---------+---------+------+ - |List Locations |Yes |Yes |Yes |Yes|Yes |Yes |Yes |Yes |Yes |Yes |[2] |[2] |Yes |Yes |Yes | - +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+-------+-------+---------+---------+------+ - |create |Yes |Yes |Yes |Yes|Yes |Yes |Yes |Yes |Yes |Yes |Yes |[1] |Yes |Yes |Yes | - +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+-------+-------+---------+---------+------+ - |destroy |Yes |Yes |Yes |Yes|Yes |Yes |Yes |Yes |Yes |Yes |[1] |[1] |Yes |Yes |Yes | - +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+-------+-------+---------+---------+------+ + +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+-------+-------+---------+---------+------+------------+ + | |AWS |CloudStack|Digital|EC2|GoGrid|JoyEnt|Linode|OpenStack|Parallels|Rackspace|Saltify|Vagrant|Softlayer|Softlayer|Aliyun|TencentCloud| + | |(Legacy)| |Ocean | | | | | | |(Legacy) | | | |Hardware | | | + +=======================+========+==========+=======+===+======+======+======+=========+=========+=========+=======+=======+=========+=========+======+============+ + |Query |Yes |Yes |Yes |Yes|Yes |Yes |Yes |Yes |Yes |Yes |[1] |[1] |Yes |Yes |Yes |Yes | + +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+-------+-------+---------+---------+------+------------+ + |Full Query |Yes |Yes |Yes |Yes|Yes |Yes |Yes |Yes |Yes |Yes |[1] |[1] |Yes |Yes |Yes |Yes | + +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+-------+-------+---------+---------+------+------------+ + |Selective Query |Yes |Yes |Yes |Yes|Yes |Yes |Yes |Yes |Yes |Yes |[1] |[1] |Yes |Yes |Yes |Yes | + +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+-------+-------+---------+---------+------+------------+ + |List Sizes |Yes |Yes |Yes |Yes|Yes |Yes |Yes |Yes |Yes |Yes |[2] |[2] |Yes |Yes |Yes |Yes | + +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+-------+-------+---------+---------+------+------------+ + |List Images |Yes |Yes |Yes |Yes|Yes |Yes |Yes |Yes |Yes |Yes |Yes |Yes |Yes |Yes |Yes |Yes | + +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+-------+-------+---------+---------+------+------------+ + |List Locations |Yes |Yes |Yes |Yes|Yes |Yes |Yes |Yes |Yes |Yes |[2] |[2] |Yes |Yes |Yes |Yes | + +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+-------+-------+---------+---------+------+------------+ + |create |Yes |Yes |Yes |Yes|Yes |Yes |Yes |Yes |Yes |Yes |Yes |[1] |Yes |Yes |Yes |Yes | + +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+-------+-------+---------+---------+------+------------+ + |destroy |Yes |Yes |Yes |Yes|Yes |Yes |Yes |Yes |Yes |Yes |[1] |[1] |Yes |Yes |Yes |Yes | + +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+-------+-------+---------+---------+------+------------+ [1] Yes, if salt-api is enabled. @@ -74,46 +74,46 @@ instance name to be passed in. For example: .. container:: scrollable - +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+ - |Actions |AWS |CloudStack|Digital|EC2|GoGrid|JoyEnt|Linode|OpenStack|Parallels|Rackspace|Saltify&|Softlayer|Softlayer|Aliyun| - | |(Legacy)| |Ocean | | | | | | |(Legacy) | Vagrant| |Hardware | | - +=======================+========+==========+=======+===+======+======+======+=========+=========+=========+========+=========+=========+======+ - |attach_volume | | | |Yes| | | | | | | | | | | - +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+ - |create_attach_volumes |Yes | | |Yes| | | | | | | | | | | - +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+ - |del_tags |Yes | | |Yes| | | | | | | | | | | - +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+ - |delvol_on_destroy | | | |Yes| | | | | | | | | | | - +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+ - |detach_volume | | | |Yes| | | | | | | | | | | - +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+ - |disable_term_protect |Yes | | |Yes| | | | | | | | | | | - +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+ - |enable_term_protect |Yes | | |Yes| | | | | | | | | | | - +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+ - |get_tags |Yes | | |Yes| | | | | | | | | | | - +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+ - |keepvol_on_destroy | | | |Yes| | | | | | | | | | | - +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+ - |list_keypairs | | |Yes | | | | | | | | | | | | - +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+ - |rename |Yes | | |Yes| | | | | | | | | | | - +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+ - |set_tags |Yes | | |Yes| | | | | | | | | | | - +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+ - |show_delvol_on_destroy | | | |Yes| | | | | | | | | | | - +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+ - |show_instance | | |Yes |Yes| | |Yes | |Yes | | |Yes |Yes |Yes | - +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+ - |show_term_protect | | | |Yes| | | | | | | | | | | - +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+ - |start |Yes | | |Yes| |Yes |Yes | |Yes | | | | |Yes | - +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+ - |stop |Yes | | |Yes| |Yes |Yes | |Yes | | | | |Yes | - +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+ - |take_action | | | | | |Yes | | | | | | | | | - +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+ + +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+------------+ + |Actions |AWS |CloudStack|Digital|EC2|GoGrid|JoyEnt|Linode|OpenStack|Parallels|Rackspace|Saltify&|Softlayer|Softlayer|Aliyun|TencentCloud| + | |(Legacy)| |Ocean | | | | | | |(Legacy) | Vagrant| |Hardware | | | + +=======================+========+==========+=======+===+======+======+======+=========+=========+=========+========+=========+=========+======+============+ + |attach_volume | | | |Yes| | | | | | | | | | | | + +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+------------+ + |create_attach_volumes |Yes | | |Yes| | | | | | | | | | | | + +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+------------+ + |del_tags |Yes | | |Yes| | | | | | | | | | | | + +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+------------+ + |delvol_on_destroy | | | |Yes| | | | | | | | | | | | + +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+------------+ + |detach_volume | | | |Yes| | | | | | | | | | | | + +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+------------+ + |disable_term_protect |Yes | | |Yes| | | | | | | | | | | | + +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+------------+ + |enable_term_protect |Yes | | |Yes| | | | | | | | | | | | + +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+------------+ + |get_tags |Yes | | |Yes| | | | | | | | | | | | + +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+------------+ + |keepvol_on_destroy | | | |Yes| | | | | | | | | | | | + +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+------------+ + |list_keypairs | | |Yes | | | | | | | | | | | | | + +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+------------+ + |rename |Yes | | |Yes| | | | | | | | | | | | + +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+------------+ + |set_tags |Yes | | |Yes| | | | | | | | | | | | + +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+------------+ + |show_delvol_on_destroy | | | |Yes| | | | | | | | | | | | + +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+------------+ + |show_instance | | |Yes |Yes| | |Yes | |Yes | | |Yes |Yes |Yes |Yes | + +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+------------+ + |show_term_protect | | | |Yes| | | | | | | | | | | | + +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+------------+ + |start |Yes | | |Yes| |Yes |Yes | |Yes | | | | |Yes |Yes | + +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+------------+ + |stop |Yes | | |Yes| |Yes |Yes | |Yes | | | | |Yes |Yes | + +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+------------+ + |take_action | | | | | |Yes | | | | | | | | | | + +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+------------+ Functions ========= @@ -126,83 +126,83 @@ require the name of the provider to be passed in. For example: .. container:: scrollable - +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+ - |Functions |AWS |CloudStack|Digital|EC2|GoGrid|JoyEnt|Linode|OpenStack|Parallels|Rackspace|Saltify&|Softlayer|Softlayer|Aliyun| - | |(Legacy)| |Ocean | | | | | | |(Legacy) | Vagrant| |Hardware | | - +=======================+========+==========+=======+===+======+======+======+=========+=========+=========+========+=========+=========+======+ - |block_device_mappings |Yes | | | | | | | | | | | | | | - +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+ - |create_keypair | | | |Yes| | | | | | | | | | | - +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+ - |create_volume | | | |Yes| | | | | | | | | | | - +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+ - |delete_key | | | | | |Yes | | | | | | | | | - +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+ - |delete_keypair | | | |Yes| | | | | | | | | | | - +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+ - |delete_volume | | | |Yes| | | | | | | | | | | - +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+ - |get_image | | |Yes | | |Yes | | |Yes | | | | |Yes | - +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+ - |get_ip | |Yes | | | | | | | | | | | | | - +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+ - |get_key | |Yes | | | | | | | | | | | | | - +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+ - |get_keyid | | |Yes | | | | | | | | | | | | - +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+ - |get_keypair | |Yes | | | | | | | | | | | | | - +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+ - |get_networkid | |Yes | | | | | | | | | | | | | - +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+ - |get_node | | | | | |Yes | | | | | | | | | - +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+ - |get_password | |Yes | | | | | | | | | | | | | - +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+ - |get_size | | |Yes | | |Yes | | | | | | | |Yes | - +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+ - |get_spot_config | | | |Yes| | | | | | | | | | | - +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+ - |get_subnetid | | | |Yes| | | | | | | | | | | - +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+ - |iam_profile |Yes | | |Yes| | | | | | | | | |Yes | - +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+ - |import_key | | | | | |Yes | | | | | | | | | - +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+ - |key_list | | | | | |Yes | | | | | | | | | - +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+ - |keyname |Yes | | |Yes| | | | | | | | | | | - +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+ - |list_availability_zones| | | |Yes| | | | | | | | | |Yes | - +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+ - |list_custom_images | | | | | | | | | | | |Yes | | | - +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+ - |list_keys | | | | | |Yes | | | | | | | | | - +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+ - |list_nodes |Yes |Yes |Yes |Yes|Yes |Yes |Yes |Yes |Yes |Yes |Yes |Yes |Yes |Yes | - +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+ - |list_nodes_full |Yes |Yes |Yes |Yes|Yes |Yes |Yes |Yes |Yes |Yes |Yes |Yes |Yes |Yes | - +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+ - |list_nodes_select |Yes |Yes |Yes |Yes|Yes |Yes |Yes |Yes |Yes |Yes |Yes |Yes |Yes |Yes | - +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+ - |list_vlans | | | | | | | | | | | |Yes |Yes | | - +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+ - |rackconnect | | | | | | | |Yes | | | | | | | - +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+ - |reboot | | | |Yes| |Yes | | | | |[1] | | |Yes | - +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+ - |reformat_node | | | | | |Yes | | | | | | | | | - +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+ - |securitygroup |Yes | | |Yes| | | | | | | | | | | - +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+ - |securitygroupid | | | |Yes| | | | | | | | | |Yes | - +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+ - |show_image | | | |Yes| | | | |Yes | | | | |Yes | - +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+ - |show_key | | | | | |Yes | | | | | | | | | - +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+ - |show_keypair | | |Yes |Yes| | | | | | | | | | | - +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+ - |show_volume | | | |Yes| | | | | | | | | |Yes | - +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+ + +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+------------+ + |Functions |AWS |CloudStack|Digital|EC2|GoGrid|JoyEnt|Linode|OpenStack|Parallels|Rackspace|Saltify&|Softlayer|Softlayer|Aliyun|TencentCloud| + | |(Legacy)| |Ocean | | | | | | |(Legacy) | Vagrant| |Hardware | | | + +=======================+========+==========+=======+===+======+======+======+=========+=========+=========+========+=========+=========+======+============+ + |block_device_mappings |Yes | | | | | | | | | | | | | | | + +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+------------+ + |create_keypair | | | |Yes| | | | | | | | | | | | + +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+------------+ + |create_volume | | | |Yes| | | | | | | | | | | | + +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+------------+ + |delete_key | | | | | |Yes | | | | | | | | | | + +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+------------+ + |delete_keypair | | | |Yes| | | | | | | | | | | | + +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+------------+ + |delete_volume | | | |Yes| | | | | | | | | | | | + +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+------------+ + |get_image | | |Yes | | |Yes | | |Yes | | | | |Yes | | + +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+------------+ + |get_ip | |Yes | | | | | | | | | | | | | | + +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+------------+ + |get_key | |Yes | | | | | | | | | | | | | | + +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+------------+ + |get_keyid | | |Yes | | | | | | | | | | | | | + +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+------------+ + |get_keypair | |Yes | | | | | | | | | | | | | | + +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+------------+ + |get_networkid | |Yes | | | | | | | | | | | | | | + +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+------------+ + |get_node | | | | | |Yes | | | | | | | | | | + +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+------------+ + |get_password | |Yes | | | | | | | | | | | | | | + +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+------------+ + |get_size | | |Yes | | |Yes | | | | | | | |Yes | | + +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+------------+ + |get_spot_config | | | |Yes| | | | | | | | | | | | + +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+------------+ + |get_subnetid | | | |Yes| | | | | | | | | | | | + +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+------------+ + |iam_profile |Yes | | |Yes| | | | | | | | | |Yes | | + +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+------------+ + |import_key | | | | | |Yes | | | | | | | | | | + +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+------------+ + |key_list | | | | | |Yes | | | | | | | | | | + +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+------------+ + |keyname |Yes | | |Yes| | | | | | | | | | | | + +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+------------+ + |list_availability_zones| | | |Yes| | | | | | | | | |Yes |Yes | + +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+------------+ + |list_custom_images | | | | | | | | | | | |Yes | | |Yes | + +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+------------+ + |list_keys | | | | | |Yes | | | | | | | | | | + +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+------------+ + |list_nodes |Yes |Yes |Yes |Yes|Yes |Yes |Yes |Yes |Yes |Yes |Yes |Yes |Yes |Yes |Yes | + +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+------------+ + |list_nodes_full |Yes |Yes |Yes |Yes|Yes |Yes |Yes |Yes |Yes |Yes |Yes |Yes |Yes |Yes |Yes | + +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+------------+ + |list_nodes_select |Yes |Yes |Yes |Yes|Yes |Yes |Yes |Yes |Yes |Yes |Yes |Yes |Yes |Yes |Yes | + +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+------------+ + |list_vlans | | | | | | | | | | | |Yes |Yes | | | + +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+------------+ + |rackconnect | | | | | | | |Yes | | | | | | | | + +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+------------+ + |reboot | | | |Yes| |Yes | | | | |[1] | | |Yes |Yes | + +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+------------+ + |reformat_node | | | | | |Yes | | | | | | | | | | + +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+------------+ + |securitygroup |Yes | | |Yes| | | | | | | | | | | | + +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+------------+ + |securitygroupid | | | |Yes| | | | | | | | | |Yes | | + +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+------------+ + |show_image | | | |Yes| | | | |Yes | | | | |Yes |Yes | + +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+------------+ + |show_key | | | | | |Yes | | | | | | | | | | + +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+------------+ + |show_keypair | | |Yes |Yes| | | | | | | | | | | | + +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+------------+ + |show_volume | | | |Yes| | | | | | | | | | | | + +-----------------------+--------+----------+-------+---+------+------+------+---------+---------+---------+--------+---------+---------+------+------------+ [1] Yes, if salt-api is enabled. diff --git a/doc/topics/cloud/index.rst b/doc/topics/cloud/index.rst index 3a6ba09900fe..f8d858eed20c 100644 --- a/doc/topics/cloud/index.rst +++ b/doc/topics/cloud/index.rst @@ -128,6 +128,7 @@ Cloud Provider Specifics Getting Started With Scaleway Getting Started With Saltify Getting Started With SoftLayer + Getting Started With TencentCloud Getting Started With Vagrant Getting Started With Vexxhost Getting Started With Virtualbox diff --git a/doc/topics/cloud/tencentcloud.rst b/doc/topics/cloud/tencentcloud.rst new file mode 100644 index 000000000000..b2f4f6317c2c --- /dev/null +++ b/doc/topics/cloud/tencentcloud.rst @@ -0,0 +1,303 @@ +================================= +Getting Started With TencentCloud +================================= + +Tencent Cloud is a secure, reliable and high-performance cloud compute service +provided by Tencent. It is the 2st largest Cloud Provider in China. + + +Dependencies +============ +The TencentCloud driver for Salt Cloud requires the tencentcloud-sdk-python package, +which is available at PyPI: + +https://pypi.org/project/tencentcloud-sdk-python/ + +This package can be installed using ``pip`` or ``easy_install``: + +.. code-block:: bash + + # pip install tencentcloud-sdk-python + # easy_install tencentcloud-sdk-python + + +Provider Configuration +====================== +To use this module, set up the cloud configuration at + ``/etc/salt/cloud.providers`` or ``/etc/salt/cloud.providers.d/*.conf``: + +.. code-block:: yaml + + my-tencentcloud-config: + driver: tencentcloud + # TencentCloud Secret Id + id: AKIDA64pOio9BMemkApzevX0HS169S4b750A + # TencentCloud Secret Key + key: 8r2xmPn0C5FDvRAlmcJimiTZKVRsk260 + # TencentCloud Region + location: ap-guangzhou + +Configuration Parameters +~~~~~~~~~~~~~~~~~~~~~~~~ + +driver +------ +Required, Please use ``tencentcloud`` always with this module. + +id +-- +Required, The TencentCloud secret id. + +key +--- +Required, The TencentCloud secret key. + +location +-------- +Optional, If this value is not specified, the default is ``ap-guangzhou``. +Location to be configured can be found using the ``--list-locations`` option: + +.. code-block:: bash + + # salt-cloud --list-location my-tencentcloud-config + + +Profile Configuration +===================== + +TencentCloud profiles require a ``provider``, ``availability_zone``, ``image`` and ``size``. +Set up an initial profile at ``/etc/salt/cloud.profiles`` or ``/etc/salt/cloud.profiles.d/*.conf``: + +.. code-block:: yaml + + tencentcloud-guangzhou-s1sm1: + provider: my-tencentcloud-config + availability_zone: ap-guangzhou-3 + image: img-31tjrtph + size: S1.SMALL1 + allocate_public_ip: True + internet_max_bandwidth_out: 1 + password: '153e41ec96140152' + securitygroups: + - sg-5e90804b + +Configuration Parameters +~~~~~~~~~~~~~~~~~~~~~~~~ + +provider +-------- +Required, Name of entry in ``salt/cloud.providers.d/???`` file. + +availability_zone +----------------- +Required, The available zone that the instance locates at. +Available zones to build an instance can be found using the ``list_availability_zones`` function: + +.. code-block:: bash + + # salt-cloud -f list_availability_zones my-tencentcloud-config + +image +----- +Required, The image id to use for the instance. +Images to build an instance can be found using the ``--list-images`` option: + +.. code-block:: bash + + # salt-cloud --list-images my-tencentcloud-config + +size +---- +Required, Instance type for instance can be found using the ``--list-sizes`` option. + +.. code-block:: bash + + # salt-cloud --list-sizes my-tencentcloud-config + +securitygroups +-------------- +Optional, A list of security group ids to associate with. +Security group ids to build an instance can be found using the ``list_securitygroups`` function: + +.. code-block:: bash + + # salt-cloud -f list_securitygroups my-tencentcloud-config + +Multiple security group is supported. + +.. code-block:: yaml + + tencentcloud-guangzhou-s1sm1: + securitygroups: + - sg-5e90804b + - sg-8kpynf2t + +hostname +-------- +Optional, The hostname of instance. + +instance_charge_type +-------------------- +Optional, The charge type of instance. Valid values are ``PREPAID``, +``POSTPAID_BY_HOUR`` and ``SPOTPAID``, The default is ``POSTPAID_BY_HOUR``. + +instance_charge_type_prepaid_renew_flag +--------------------------------------- +Optional, When enabled, the instance will be renew automatically +when it reach the end of the prepaid tenancy. +Valid values are ``NOTIFY_AND_AUTO_RENEW``, ``NOTIFY_AND_MANUAL_RENEW`` and ``DISABLE_NOTIFY_AND_MANUAL_RENEW``. +NOTE: it only works when ``instance_charge_type`` is set to ``PREPAID``. + +instance_charge_type_prepaid_period +----------------------------------- +Optional, The tenancy (time unit is month) of the prepaid instance, +Valid values are ``1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 24, 36``. +NOTE: it only works when ``instance_charge_type`` is set to ``PREPAID``. + +allocate_public_ip +------------------ +Optional, Associate a public ip address with an instance +in a VPC or Classic. Boolean value, Default is ``false``. + +internet_max_bandwidth_out +-------------------------- +Optional, Maximum outgoing bandwidth to the public network, measured in Mbps (Mega bit per second). +Value range: ``[0, 100]``, If this value is not specified, then automatically sets it to ``0 Mbps``. + +internet_charge_type +-------------------- +Optional, Internet charge type of the instance, Valid values are ``BANDWIDTH_PREPAID``, +``TRAFFIC_POSTPAID_BY_HOUR``, ``BANDWIDTH_POSTPAID_BY_HOUR`` and ``BANDWIDTH_PACKAGE``. +The default is ``TRAFFIC_POSTPAID_BY_HOUR``. + +key_name +-------- +Optional, The key pair to use for the instance, it looks like ``skey-16jig7tx``. + +password +-------- +Optional, Password to an instance. + +private_ip +---------- +Optional, The private ip to be assigned to this instance, +must be in the provided subnet and available. + +project_id +---------- +Optional, The project instance belongs to, default to ``0``. + +vpc_id +------ +Optional, The id of a VPC network. +If you want to create instances in VPC network, this parameter must be set. + +subnet_id +--------- +Optional, The id of a VPC subnetwork. +If you want to create instances in VPC network, this parameter must be set. + +system_disk_size +---------------- +Optional, Size of the system disk. +Value range: ``[50, 1000]``, and unit is ``GB``. Default is ``50 GB``. + +system_disk_type +---------------- +Optional, Type of the system disk. +Valid values are ``CLOUD_BASIC``, ``CLOUD_SSD`` and ``CLOUD_PREMIUM``, default value is ``CLOUD_BASIC``. + + +Actions +======= +The following actions are currently supported by the TencentCloud Salt Cloud driver. + +show_instance +~~~~~~~~~~~~~ +This action is a thin wrapper around `--full-query`, which displays details on a +single instance only. In an environment with several machines, this will save a +user from having to sort through all instance data, just to examine a single +instance. + +.. code-block:: bash + + $ salt-cloud -a show_instance myinstance + +show_disk +~~~~~~~~~ +Return details about a specific instance. + +.. code-block:: bash + + $ salt-cloud -a show_disk myinstance + +destroy +~~~~~~~ +Destroy a TencentCloud instance. + +.. code-block:: bash + + $ salt-cloud -a destroy myinstance + +start +~~~~~ +Start a TencentCloud instance. + +.. code-block:: bash + + $ salt-cloud -a start myinstance + +stop +~~~~ +Stop a TencentCloud instance. + +.. code-block:: bash + + $ salt-cloud -a stop myinstance + +reboot +~~~~~~ +Reboot a TencentCloud instance. + +.. code-block:: bash + + $ salt-cloud -a reboot myinstance + + +Functions +========= +The following functions are currently supported by the TencentCloud Salt Cloud driver. + +list_securitygroups +~~~~~~~~~~~~~~~~~~~ +This function return all TencentCloud security groups in current region. + +.. code-block:: bash + + $ salt-cloud -f list_securitygroups my-tencentcloud-config + +list_availability_zones +~~~~~~~~~~~~~~~~~~~~~~~ +This function return all TencentCloud availability zones in current region. + +.. code-block:: bash + + $ salt-cloud -f list_availability_zones my-tencentcloud-config + +list_custom_images +~~~~~~~~~~~~~~~~~~ +This function lists any custom images associated with the account, that can +be used to create a new instance. + +.. code-block:: bash + + $ salt-cloud -f list_custom_images my-tencentcloud-config + +show_image +~~~~~~~~~~ +Return details about a specific image, that can +be used to create a new instance. + +.. code-block:: bash + + $ salt-cloud -f show_image tencentcloud image=img-31tjrtph diff --git a/salt/cloud/clouds/tencentcloud.py b/salt/cloud/clouds/tencentcloud.py new file mode 100644 index 000000000000..7ad0bd00fedd --- /dev/null +++ b/salt/cloud/clouds/tencentcloud.py @@ -0,0 +1,1029 @@ +# -*- coding: utf-8 -*- + +''' +TencentCloud Cloud Module +============================= + +.. versionadded:: 2019.09.01 + +The TencentCloud Cloud Module is used to control access to the TencentCloud instance. +https://intl.cloud.tencent.com/ + +To use this module, set up the cloud configuration at + ``/etc/salt/cloud.providers`` or ``/etc/salt/cloud.providers.d/*.conf``: + +.. code-block:: yaml + + my-tencentcloud-config: + driver: tencentcloud + # TencentCloud Secret Id + id: AKIDA64pOio9BMemkApzevX0HS169S4b750A + # TencentCloud Secret Key + key: 8r2xmPn0C5FDvRAlmcJimiTZKVRsk260 + # TencentCloud Region + location: ap-guangzhou + +:depends: tencentcloud-sdk-python +''' + +# pylint: disable=invalid-name,redefined-builtin,function-redefined,undefined-variable,broad-except,too-many-locals,too-many-branches + +# Import python libs +from __future__ import absolute_import, print_function, unicode_literals +import time +import pprint +import logging + +# Import salt cloud libs +import salt.utils.cloud +import salt.utils.data +import salt.utils.json +import salt.config as config +from salt.exceptions import ( + SaltCloudNotFound, + SaltCloudSystemExit, + SaltCloudExecutionFailure, + SaltCloudExecutionTimeout +) + +# Import 3rd-party libs +from salt.ext import six +from salt.ext.six.moves import range + +try: + # Try import tencentcloud sdk + from tencentcloud.common import credential + from tencentcloud.common.profile.client_profile import ClientProfile + from tencentcloud.cvm.v20170312 import cvm_client + from tencentcloud.cvm.v20170312 import models as cvm_models + from tencentcloud.vpc.v20170312 import vpc_client + from tencentcloud.vpc.v20170312 import models as vpc_models + HAS_TENCENTCLOUD_SDK = True +except ImportError: + HAS_TENCENTCLOUD_SDK = False + +# Get logging started +log = logging.getLogger(__name__) + +# The default region +DEFAULT_REGION = 'ap-guangzhou' + +# The TencentCloud +__virtualname__ = 'tencentcloud' + + +def __virtual__(): + ''' + Only load in this module if the TencentCloud configurations are in place + ''' + if get_configured_provider() is False: + return False + + if get_dependencies() is False: + return False + + return __virtualname__ + + +def get_configured_provider(): + ''' + Return the first configured instance. + ''' + return config.is_provider_configured( + __opts__, + __active_provider_name__ or __virtualname__, + ('id', 'key') + ) + + +def get_dependencies(): + ''' + Warn if dependencies aren't met. + ''' + return config.check_driver_dependencies( + __virtualname__, + {'tencentcloud-sdk-python': HAS_TENCENTCLOUD_SDK} + ) + + +def get_provider_client(name=None): + ''' + Return a new provider client + ''' + provider = get_configured_provider() + + secretId = provider.get('id') + secretKey = provider.get('key') + region = __get_location(None) + + cpf = ClientProfile() + cpf.language = "en-US" + crd = credential.Credential(secretId, secretKey) + + if name == "cvm_client": + client = cvm_client.CvmClient(crd, region, cpf) + elif name == "vpc_client": + client = vpc_client.VpcClient(crd, region, cpf) + else: + raise SaltCloudSystemExit( + 'Client name {0} is not supported'.format(name) + ) + + return client + + +def avail_locations(call=None): + ''' + Return TencentCloud available region + + CLI Example: + + .. code-block:: bash + + salt-cloud --list-locations my-tencentcloud-config + salt-cloud -f avail_locations my-tencentcloud-config + ''' + if call == 'action': + raise SaltCloudSystemExit( + 'The avail_locations function must be called with ' + '-f or --function, or with the --list-locations option' + ) + + client = get_provider_client("cvm_client") + req = cvm_models.DescribeRegionsRequest() + resp = client.DescribeRegions(req) + + ret = {} + for region in resp.RegionSet: + if region.RegionState != "AVAILABLE": + continue + ret[region.Region] = region.RegionName + + return ret + + +def avail_images(call=None): + ''' + Return TencentCloud available image + + CLI Example: + + .. code-block:: bash + + salt-cloud --list-images my-tencentcloud-config + salt-cloud -f avail_images my-tencentcloud-config + ''' + if call == 'action': + raise SaltCloudSystemExit( + 'The avail_images function must be called with ' + '-f or --function, or with the --list-images option' + ) + + return _get_images(["PUBLIC_IMAGE", "PRIVATE_IMAGE", "IMPORT_IMAGE", "SHARED_IMAGE"]) + + +def avail_sizes(call=None): + ''' + Return TencentCloud available instance type + + CLI Example: + + .. code-block:: bash + + salt-cloud --list-sizes my-tencentcloud-config + salt-cloud -f avail_sizes my-tencentcloud-config + ''' + if call == 'action': + raise SaltCloudSystemExit( + 'The avail_sizes function must be called with ' + '-f or --function, or with the --list-sizes option' + ) + + client = get_provider_client("cvm_client") + req = cvm_models.DescribeInstanceTypeConfigsRequest() + resp = client.DescribeInstanceTypeConfigs(req) + + ret = {} + for typeConfig in resp.InstanceTypeConfigSet: + ret[typeConfig.InstanceType] = { + "Zone": typeConfig.Zone, + "InstanceFamily": typeConfig.InstanceFamily, + "Memory": "{0}GB".format(typeConfig.Memory), + "CPU": "{0}-Core".format(typeConfig.CPU), + } + if typeConfig.GPU: + ret[typeConfig.InstanceType]["GPU"] = "{0}-Core".format( + typeConfig.GPU) + + return ret + + +def list_securitygroups(call=None): + ''' + Return all TencentCloud security groups in current region + + CLI Example: + + .. code-block:: bash + + salt-cloud -f list_securitygroups my-tencentcloud-config + ''' + if call == 'action': + raise SaltCloudSystemExit( + 'The list_securitygroups function must be called with -f or --function.' + ) + + client = get_provider_client("vpc_client") + req = vpc_models.DescribeSecurityGroupsRequest() + req.Offset = 0 + req.Limit = 100 + resp = client.DescribeSecurityGroups(req) + + ret = {} + for sg in resp.SecurityGroupSet: + ret[sg.SecurityGroupId] = { + "SecurityGroupName": sg.SecurityGroupName, + "SecurityGroupDesc": sg.SecurityGroupDesc, + "ProjectId": sg.ProjectId, + "IsDefault": sg.IsDefault, + "CreatedTime": sg.CreatedTime, + } + + return ret + + +def list_custom_images(call=None): + ''' + Return all TencentCloud images in current region + + CLI Example: + + .. code-block:: bash + + salt-cloud -f list_custom_images my-tencentcloud-config + ''' + if call == 'action': + raise SaltCloudSystemExit( + 'The list_custom_images function must be called with -f or --function.' + ) + + return _get_images(["PRIVATE_IMAGE", "IMPORT_IMAGE"]) + + +def list_availability_zones(call=None): + ''' + Return all TencentCloud availability zones in current region + + CLI Example: + + .. code-block:: bash + + salt-cloud -f list_availability_zones my-tencentcloud-config + ''' + if call == 'action': + raise SaltCloudSystemExit( + 'The list_availability_zones function must be called with -f or --function.' + ) + + client = get_provider_client("cvm_client") + req = cvm_models.DescribeZonesRequest() + resp = client.DescribeZones(req) + + ret = {} + for zone in resp.ZoneSet: + if zone.ZoneState != "AVAILABLE": + continue + ret[zone.Zone] = zone.ZoneName, + + return ret + + +def list_nodes(call=None): + ''' + Return a list of instances that are on the provider + + CLI Examples: + + .. code-block:: bash + + salt-cloud -Q + ''' + if call == 'action': + raise SaltCloudSystemExit( + 'The list_nodes function must be called with -f or --function.' + ) + + ret = {} + nodes = _get_nodes() + for instance in nodes: + ret[instance.InstanceId] = { + "InstanceId": instance.InstanceId, + "InstanceName": instance.InstanceName, + "InstanceType": instance.InstanceType, + "ImageId": instance.ImageId, + "PublicIpAddresses": instance.PublicIpAddresses, + "PrivateIpAddresses": instance.PrivateIpAddresses, + "InstanceState": instance.InstanceState, + } + + return ret + + +def list_nodes_full(call=None): + ''' + Return a list of instances that are on the provider, with full details + + CLI Examples: + + .. code-block:: bash + + salt-cloud -F + ''' + if call == 'action': + raise SaltCloudSystemExit( + 'The list_nodes_full function must be called with -f or --function.' + ) + + ret = {} + nodes = _get_nodes() + for instance in nodes: + instanceAttribute = vars(instance) + ret[instance.InstanceName] = instanceAttribute + for k in ["DataDisks", "InternetAccessible", "LoginSettings", + "Placement", "SystemDisk", "Tags", "VirtualPrivateCloud"]: + ret[instance.InstanceName][k] = six.text_type(instanceAttribute[k]) + + provider = __active_provider_name__ or 'tencentcloud' + if ':' in provider: + comps = provider.split(':') + provider = comps[0] + + __opts__['update_cachedir'] = True + __utils__['cloud.cache_node_list'](ret, provider, __opts__) + + return ret + + +def list_nodes_select(call=None): + ''' + Return a list of instances that are on the provider, with select fields + + CLI Examples: + + .. code-block:: bash + + salt-cloud -S + ''' + return salt.utils.cloud.list_nodes_select( + list_nodes_full('function'), __opts__['query.selection'], call, + ) + + +def list_nodes_min(call=None): + ''' + Return a list of instances that are on the provider, Only names, and their state, is returned. + + CLI Examples: + + .. code-block:: bash + + salt-cloud -f list_nodes_min my-tencentcloud-config + ''' + if call == 'action': + raise SaltCloudSystemExit( + 'The list_nodes_min function must be called with -f or --function.' + ) + + ret = {} + nodes = _get_nodes() + for instance in nodes: + ret[instance.InstanceName] = { + "InstanceId": instance.InstanceId, + "InstanceState": instance.InstanceState, + } + + return ret + + +def create(vm_): + ''' + Create a single TencentCloud instance from a data dict. + + TencentCloud profiles require a ``provider``, ``availability_zone``, ``image`` and ``size``. + Set up profile at ``/etc/salt/cloud.profiles`` or ``/etc/salt/cloud.profiles.d/*.conf``: + + .. code-block:: yaml + + tencentcloud-guangzhou-s1sm1: + provider: my-tencentcloud-config + availability_zone: ap-guangzhou-3 + image: img-31tjrtph + size: S1.SMALL1 + allocate_public_ip: True + internet_max_bandwidth_out: 1 + password: '153e41ec96140152' + securitygroups: + - sg-5e90804b + + CLI Examples: + + .. code-block:: bash + + salt-cloud -p tencentcloud-guangzhou-s1 myinstance + ''' + try: + # Check for required profile parameters before sending any API calls. + if vm_['profile'] and \ + config.is_profile_configured(__opts__, + __active_provider_name__ or 'tencentcloud', + vm_['profile'], + vm_=vm_) is False: + return False + except AttributeError: + pass + + __utils__['cloud.fire_event']( + 'event', + 'starting create', + 'salt/cloud/{0}/creating'.format(vm_['name']), + args=__utils__['cloud.filter_event']( + 'creating', vm_, ['name', 'profile', 'provider', 'driver']), + sock_dir=__opts__['sock_dir'], + transport=__opts__['transport'] + ) + + log.debug('Try creating instance: %s', pprint.pformat(vm_)) + + # Init cvm client + client = get_provider_client("cvm_client") + req = cvm_models.RunInstancesRequest() + req.InstanceName = vm_['name'] + + # Required parameters + req.InstanceType = __get_size(vm_) + req.ImageId = __get_image(vm_) + + zone = __get_availability_zone(vm_) + projectId = vm_.get("project_id", 0) + req.Placement = {"Zone": zone, "ProjectId": projectId} + + # Optional parameters + + req.SecurityGroupIds = __get_securitygroups(vm_) + req.HostName = vm_.get("hostname", vm_['name']) + + req.InstanceChargeType = vm_.get( + "instance_charge_type", "POSTPAID_BY_HOUR") + if req.InstanceChargeType == "PREPAID": + period = vm_.get( + "instance_charge_type_prepaid_period", 1) + renewFlag = vm_.get("instance_charge_type_prepaid_renew_flag", + "NOTIFY_AND_MANUAL_RENEW") + req.InstanceChargePrepaid = {"Period": period, "RenewFlag": renewFlag} + + allocate_public_ip = vm_.get("allocate_public_ip", False) + internet_max_bandwidth_out = vm_.get("internet_max_bandwidth_out", 0) + if allocate_public_ip and internet_max_bandwidth_out > 0: + req.InternetAccessible = {"PublicIpAssigned": allocate_public_ip, + "InternetMaxBandwidthOut": internet_max_bandwidth_out} + internet_charge_type = vm_.get("internet_charge_type", "") + if internet_charge_type != "": + req.InternetAccessible["InternetChargeType"] = internet_charge_type + + req.LoginSettings = {} + req.VirtualPrivateCloud = {} + req.SystemDisk = {} + + keyId = vm_.get("key_name", "") + if keyId: + req.LoginSettings["KeyIds"] = [keyId] + + password = vm_.get("password", "") + if password: + req.LoginSettings["Password"] = password + + private_ip = vm_.get("private_ip", "") + if private_ip: + req.VirtualPrivateCloud["PrivateIpAddresses"] = private_ip + + vpc_id = vm_.get("vpc_id", "") + if vpc_id: + req.VirtualPrivateCloud["VpcId"] = vpc_id + + subnetId = vm_.get("subnet_id", "") + if subnetId: + req.VirtualPrivateCloud["SubnetId"] = subnetId + + system_disk_size = vm_.get("system_disk_size", 0) + if system_disk_size: + req.SystemDisk["DiskSize"] = system_disk_size + + system_disk_type = vm_.get("system_disk_type", "") + if system_disk_type: + req.SystemDisk["DiskType"] = system_disk_type + + __utils__['cloud.fire_event']( + 'event', + 'requesting instance', + 'salt/cloud/{0}/requesting'.format(vm_['name']), + args=__utils__['cloud.filter_event']( + 'requesting', vm_, list(vm_)), + sock_dir=__opts__['sock_dir'], + transport=__opts__['transport'] + ) + + try: + resp = client.RunInstances(req) + if not resp.InstanceIdSet: + raise SaltCloudSystemExit( + "Unexpected error, no instance created" + ) + except Exception as exc: + log.error( + 'Error creating %s on tencentcloud\n\n' + 'The following exception was thrown when trying to ' + 'run the initial deployment: %s', + vm_['name'], six.text_type(exc), + # Show the traceback if the debug logging level is enabled + exc_info_on_loglevel=logging.DEBUG + ) + return False + + time.sleep(5) + + def __query_node_data(vm_name): + data = show_instance(vm_name, call='action') + if not data: + return False + if data["InstanceState"] != "RUNNING": + return False + if data["PrivateIpAddresses"]: + return data + + try: + data = salt.utils.cloud.wait_for_ip( + __query_node_data, + update_args=(vm_['name'],), + timeout=config.get_cloud_config_value( + 'wait_for_ip_timeout', vm_, __opts__, default=10 * 60), + interval=config.get_cloud_config_value( + 'wait_for_ip_interval', vm_, __opts__, default=10), + ) + except (SaltCloudExecutionTimeout, SaltCloudExecutionFailure) as exc: + try: + destroy(vm_['name']) + except SaltCloudSystemExit: + pass + finally: + raise SaltCloudSystemExit(six.text_type(exc)) + + if data["PublicIpAddresses"]: + ssh_ip = data["PublicIpAddresses"][0] + elif data["PrivateIpAddresses"]: + ssh_ip = data["PrivateIpAddresses"][0] + else: + log.error('No available ip: cant connect to salt') + return False + + log.debug('Instance %s: %s is now running', vm_['name'], ssh_ip) + vm_['ssh_host'] = ssh_ip + + # The instance is booted and accessible, let's Salt it! + ret = __utils__['cloud.bootstrap'](vm_, __opts__) + ret.update(data) + + log.debug('\'%s\' instance creation details:\n%s', + vm_['name'], pprint.pformat(data)) + + __utils__['cloud.fire_event']( + 'event', + 'created instance', + 'salt/cloud/{0}/created'.format(vm_['name']), + args=__utils__['cloud.filter_event']( + 'created', vm_, ['name', 'profile', 'provider', 'driver']), + sock_dir=__opts__['sock_dir'], + transport=__opts__['transport'] + ) + + return ret + + +def start(name, call=None): + ''' + Start a TencentCloud instance + Notice: the instance state must be stopped + + CLI Examples: + + .. code-block:: bash + + salt-cloud -a start myinstance + ''' + if call != 'action': + raise SaltCloudSystemExit( + 'The stop action must be called with -a or --action.' + ) + + node = _get_node(name) + + client = get_provider_client("cvm_client") + req = cvm_models.StartInstancesRequest() + req.InstanceIds = [node.InstanceId] + resp = client.StartInstances(req) + + return resp + + +def stop(name, force=False, call=None): + ''' + Stop a TencentCloud running instance + Note: use `force=True` to make force stop + + CLI Examples: + + .. code-block:: bash + + salt-cloud -a stop myinstance + salt-cloud -a stop myinstance force=True + ''' + if call != 'action': + raise SaltCloudSystemExit( + 'The stop action must be called with -a or --action.' + ) + + node = _get_node(name) + + client = get_provider_client("cvm_client") + req = cvm_models.StopInstancesRequest() + req.InstanceIds = [node.InstanceId] + if force: + req.ForceStop = "TRUE" + resp = client.StopInstances(req) + + return resp + + +def reboot(name, call=None): + ''' + Reboot a TencentCloud instance + + CLI Examples: + + .. code-block:: bash + + salt-cloud -a reboot myinstance + ''' + if call != 'action': + raise SaltCloudSystemExit( + 'The stop action must be called with -a or --action.' + ) + + node = _get_node(name) + + client = get_provider_client("cvm_client") + req = cvm_models.RebootInstancesRequest() + req.InstanceIds = [node.InstanceId] + resp = client.RebootInstances(req) + + return resp + + +def destroy(name, call=None): + ''' + Destroy a TencentCloud instance + + CLI Example: + + .. code-block:: bash + + salt-cloud -a destroy myinstance + salt-cloud -d myinstance + ''' + if call == 'function': + raise SaltCloudSystemExit( + 'The destroy action must be called with -d, --destroy, -a or --action.' + ) + + __utils__['cloud.fire_event']( + 'event', + 'destroying instance', + 'salt/cloud/{0}/destroying'.format(name), + args={'name': name}, + sock_dir=__opts__['sock_dir'], + transport=__opts__['transport'] + ) + + node = _get_node(name) + + client = get_provider_client("cvm_client") + req = cvm_models.TerminateInstancesRequest() + req.InstanceIds = [node.InstanceId] + resp = client.TerminateInstances(req) + + __utils__['cloud.fire_event']( + 'event', + 'destroyed instance', + 'salt/cloud/{0}/destroyed'.format(name), + args={'name': name}, + sock_dir=__opts__['sock_dir'], + transport=__opts__['transport'] + ) + + return resp + + +def script(vm_): + ''' + Return the script deployment object + ''' + return salt.utils.cloud.os_script( + config.get_cloud_config_value('script', vm_, __opts__), + vm_, + __opts__, + salt.utils.cloud.salt_config_to_yaml( + salt.utils.cloud.minion_config(__opts__, vm_) + ) + ) + + +def show_image(kwargs, call=None): + ''' + Show the details of TencentCloud image + + CLI Examples: + + .. code-block:: bash + + salt-cloud -f show_image tencentcloud image=img-31tjrtph + ''' + if call != 'function': + raise SaltCloudSystemExit( + 'The show_image function must be called with -f or --function' + ) + + if not isinstance(kwargs, dict): + kwargs = {} + + if 'image' not in kwargs: + raise SaltCloudSystemExit('No image specified.') + + image = kwargs['image'] + + client = get_provider_client("cvm_client") + req = cvm_models.DescribeImagesRequest() + req.ImageIds = [image] + resp = client.DescribeImages(req) + + if not resp.ImageSet: + raise SaltCloudNotFound( + 'The specified image \'{0}\' could not be found.'.format(image) + ) + + ret = {} + for image in resp.ImageSet: + ret[image.ImageId] = { + "ImageName": image.ImageName, + "ImageType": image.ImageType, + "ImageSource": image.ImageSource, + "Platform": image.Platform, + "Architecture": image.Architecture, + "ImageSize": "{0}GB".format(image.ImageSize), + "ImageState": image.ImageState, + } + + return ret + + +def show_instance(name, call=None): + ''' + Show the details of TencentCloud instance + + CLI Examples: + + .. code-block:: bash + + salt-cloud -a show_instance myinstance + ''' + if call != 'action': + raise SaltCloudSystemExit( + 'The show_instance action must be called with -a or --action.' + ) + + node = _get_node(name) + ret = vars(node) + for k in ["DataDisks", "InternetAccessible", "LoginSettings", + "Placement", "SystemDisk", "Tags", "VirtualPrivateCloud"]: + ret[k] = six.text_type(ret[k]) + + return ret + + +def show_disk(name, call=None): + ''' + Show the disk details of TencentCloud instance + + CLI Examples: + + .. code-block:: bash + + salt-cloud -a show_disk myinstance + ''' + if call != 'action': + raise SaltCloudSystemExit( + 'The show_disks action must be called with -a or --action.' + ) + + node = _get_node(name) + + ret = {} + ret[node.SystemDisk.DiskId] = { + "SystemDisk": True, + "DiskSize": node.SystemDisk.DiskSize, + "DiskType": node.SystemDisk.DiskType, + "DeleteWithInstance": True, + "SnapshotId": "", + } + + if node.DataDisks: + for disk in node.DataDisks: + ret[disk.DiskId] = { + "SystemDisk": False, + "DiskSize": disk.DiskSize, + "DiskType": disk.DiskType, + "DeleteWithInstance": disk.DeleteWithInstance, + "SnapshotId": disk.SnapshotId, + } + + return ret + + +def _get_node(name): + ''' + Return TencentCloud instance detail by name + ''' + attempts = 5 + while attempts >= 0: + try: + client = get_provider_client("cvm_client") + req = cvm_models.DescribeInstancesRequest() + req.Filters = [{"Name": "instance-name", "Values": [name]}] + resp = client.DescribeInstances(req) + return resp.InstanceSet[0] + except Exception as ex: + attempts -= 1 + log.debug( + 'Failed to get data for node \'%s\': %s. Remaining attempts: %d', name, ex, attempts + ) + time.sleep(0.5) + + raise SaltCloudNotFound( + 'Failed to get instance info {0}'.format(name) + ) + + +def _get_nodes(): + ''' + Return all list of TencentCloud instances + ''' + ret = [] + offset = 0 + limit = 100 + + while True: + client = get_provider_client("cvm_client") + req = cvm_models.DescribeInstancesRequest() + req.Offset = offset + req.Limit = limit + resp = client.DescribeInstances(req) + for v in resp.InstanceSet: + ret.append(v) + if len(ret) >= resp.TotalCount: + break + offset += len(resp.InstanceSet) + + return ret + + +def _get_images(image_type): + ''' + Return all list of TencentCloud images + ''' + client = get_provider_client("cvm_client") + req = cvm_models.DescribeImagesRequest() + req.Filters = [{"Name": "image-type", "Values": image_type}] + req.Offset = 0 + req.Limit = 100 + resp = client.DescribeImages(req) + + ret = {} + for image in resp.ImageSet: + if image.ImageState != "NORMAL": + continue + ret[image.ImageId] = { + "ImageName": image.ImageName, + "ImageType": image.ImageType, + "ImageSource": image.ImageSource, + "Platform": image.Platform, + "Architecture": image.Architecture, + "ImageSize": "{0}GB".format(image.ImageSize), + } + + return ret + + +def __get_image(vm_): + vm_image = six.text_type(config.get_cloud_config_value( + 'image', vm_, __opts__, search_global=False + )) + + if not vm_image: + raise SaltCloudNotFound('No image specified.') + + images = avail_images() + if vm_image in images: + return vm_image + + raise SaltCloudNotFound( + 'The specified image \'{0}\' could not be found.'.format(vm_image) + ) + + +def __get_size(vm_): + vm_size = six.text_type(config.get_cloud_config_value( + 'size', vm_, __opts__, search_global=False + )) + + if not vm_size: + raise SaltCloudNotFound('No size specified.') + + sizes = avail_sizes() + if vm_size in sizes: + return vm_size + + raise SaltCloudNotFound( + 'The specified size \'{0}\' could not be found.'.format(vm_size) + ) + + +def __get_securitygroups(vm_): + vm_securitygroups = config.get_cloud_config_value( + 'securitygroups', vm_, __opts__, search_global=False + ) + + if not vm_securitygroups: + return [] + + securitygroups = list_securitygroups() + for i in range(len(vm_securitygroups)): + vm_securitygroups[i] = six.text_type(vm_securitygroups[i]) + if vm_securitygroups[i] not in securitygroups: + raise SaltCloudNotFound( + 'The specified securitygroups \'{0}\' could not be found.'.format( + vm_securitygroups[i] + ) + ) + + return vm_securitygroups + + +def __get_availability_zone(vm_): + vm_availability_zone = six.text_type(config.get_cloud_config_value( + 'availability_zone', vm_, __opts__, search_global=False + )) + + if not vm_availability_zone: + raise SaltCloudNotFound('No availability_zone specified.') + + availability_zones = list_availability_zones() + if vm_availability_zone in availability_zones: + return vm_availability_zone + + raise SaltCloudNotFound( + 'The specified availability_zone \'{0}\' could not be found.'.format( + vm_availability_zone + ) + ) + + +def __get_location(vm_): + ''' + Return the TencentCloud region to use, in this order: + - CLI parameter + - VM parameter + - Cloud profile setting + ''' + vm_location = six.text_type(__opts__.get( + 'location', + config.get_cloud_config_value( + 'location', + vm_ or get_configured_provider(), + __opts__, + default=DEFAULT_REGION, + search_global=False + ) + )) + + if not vm_location: + raise SaltCloudNotFound('No location specified.') + + return vm_location diff --git a/tests/integration/cloud/clouds/test_tencentcloud.py b/tests/integration/cloud/clouds/test_tencentcloud.py new file mode 100644 index 000000000000..9c1ef344f95e --- /dev/null +++ b/tests/integration/cloud/clouds/test_tencentcloud.py @@ -0,0 +1,100 @@ +# -*- coding: utf-8 -*- +''' + :codeauthor: Li Kexian +''' + +# Import Python Libs +from __future__ import absolute_import, print_function, unicode_literals +import os + +# Import Salt Libs +from salt.config import cloud_providers_config + +# Import Salt Testing Libs +from tests.support.case import ShellCase +from tests.support.runtests import RUNTIME_VARS +from tests.support.helpers import expensiveTest, generate_random_name + + +# Create the cloud instance name to be used throughout the tests +INSTANCE_NAME = generate_random_name('CLOUD-TEST-') +PROVIDER_NAME = 'tencentcloud' + + +@expensiveTest +class TencentCloudTest(ShellCase): + ''' + Integration tests for the TencentCloud cloud provider in Salt-Cloud + ''' + + def setUp(self): + ''' + Sets up the test requirements + ''' + super(TencentCloudTest, self).setUp() + + # check if appropriate cloud provider and profile files are present + profile_str = 'tencentcloud-config' + providers = self.run_cloud('--list-providers') + + if profile_str + ':' not in providers: + self.skipTest( + 'Configuration file for {0} was not found. Check {0}.conf files ' + 'in tests/integration/files/conf/cloud.*.d/ to run these tests.' + .format(PROVIDER_NAME) + ) + + # check if personal access token, ssh_key_file, and ssh_key_names are present + config = cloud_providers_config( + os.path.join( + RUNTIME_VARS.FILES, + 'conf', + 'cloud.providers.d', + PROVIDER_NAME + '.conf' + ) + ) + + tid = config[profile_str][PROVIDER_NAME]['id'] + key = config[profile_str][PROVIDER_NAME]['key'] + if tid == '' or key == '': + self.skipTest( + 'An api id and key must be provided to run these tests. Check ' + 'tests/integration/files/conf/cloud.providers.d/{0}.conf'.format( + PROVIDER_NAME + ) + ) + + def test_instance(self): + ''' + Test creating an instance on TencentCloud + ''' + # check if instance with salt installed returned + try: + self.assertIn( + INSTANCE_NAME, + [i.strip() for i in self.run_cloud( + '-p tencentcloud-test {0}'.format(INSTANCE_NAME), timeout=500)] + ) + except AssertionError: + self.run_cloud( + '-d {0} --assume-yes'.format(INSTANCE_NAME), timeout=500) + raise + + # delete the instance + self.assertIn( + INSTANCE_NAME + ':', + [i.strip() for i in self.run_cloud( + '-d {0} --assume-yes'.format(INSTANCE_NAME), timeout=500)] + ) + + def tearDown(self): + ''' + Clean up after tests + ''' + query = self.run_cloud('--query') + ret_str = ' {0}:'.format(INSTANCE_NAME) + + # if test instance is still present, delete it + if ret_str in query: + self.run_cloud( + '-d {0} --assume-yes'.format(INSTANCE_NAME), timeout=500) diff --git a/tests/integration/files/conf/cloud.profiles.d/tencentcloud.conf b/tests/integration/files/conf/cloud.profiles.d/tencentcloud.conf new file mode 100644 index 000000000000..06e259f58d98 --- /dev/null +++ b/tests/integration/files/conf/cloud.profiles.d/tencentcloud.conf @@ -0,0 +1,8 @@ +tencentcloud-test: + provider: tencentcloud-config + availability_zone: ap-beijing-1 + image: img-31tjrtph + size: S1.SMALL1 + allocate_public_ip: true + internet_max_bandwidth_out: 1 + password: "@m5$z%.^8&9*as" diff --git a/tests/integration/files/conf/cloud.providers.d/tencentcloud.conf b/tests/integration/files/conf/cloud.providers.d/tencentcloud.conf new file mode 100644 index 000000000000..eaa646f83eef --- /dev/null +++ b/tests/integration/files/conf/cloud.providers.d/tencentcloud.conf @@ -0,0 +1,5 @@ +tencentcloud-config: + driver: tencentcloud + id: '' + key: '' + location: 'ap-beijing' diff --git a/tests/unit/test_config.py b/tests/unit/test_config.py index a8c015d4a02b..b3d4b545f65e 100644 --- a/tests/unit/test_config.py +++ b/tests/unit/test_config.py @@ -1142,6 +1142,11 @@ def test_apply_cloud_providers_config_extend(self): {'apikey': 'abcdefghijklmnopqrstuvwxyz', 'password': 'supersecret', 'driver': 'linode' + }, + {'id': 'a-tencentcloud-id', + 'key': 'a-tencentcloud-key', + 'location': 'ap-guangzhou', + 'driver': 'tencentcloud' }], 'conf_file': PATH} ret = {'my-production-envs': @@ -1158,6 +1163,12 @@ def test_apply_cloud_providers_config_extend(self): 'password': 'supersecret', 'profiles': {}, 'driver': 'linode'}, + 'tencentcloud': + {'id': 'a-tencentcloud-id', + 'key': 'a-tencentcloud-key', + 'location': 'ap-guangzhou', + 'profiles': {}, + 'driver': 'tencentcloud'}, 'ec2': {'profiles': {}, 'location': 'ap-southeast-1', @@ -1181,7 +1192,11 @@ def test_apply_cloud_providers_config_extend_multiple(self): {'password': 'new-password', 'extends': 'my-dev-envs:linode', 'location': 'Salt Lake City' - }], + }, + {'extends': 'my-dev-envs:tencentcloud', + 'id': 'new-id', + 'key': 'new-key', + 'location': 'ap-beijing'}], 'my-dev-envs': [{'id': 'ABCDEFGHIJKLMNOP', 'user': 'user@mycorp.com', @@ -1190,7 +1205,11 @@ def test_apply_cloud_providers_config_extend_multiple(self): 'driver': 'ec2'}, {'apikey': 'abcdefghijklmnopqrstuvwxyz', 'password': 'supersecret', - 'driver': 'linode'}], + 'driver': 'linode'}, + {'id': 'the-tencentcloud-id', + 'location': 'ap-beijing', + 'key': 'the-tencentcloud-key', + 'driver': 'tencentcloud'}], 'conf_file': PATH} ret = {'my-production-envs': {'linode': @@ -1205,7 +1224,13 @@ def test_apply_cloud_providers_config_extend_multiple(self): 'driver': 'ec2', 'id': 'ABCDEFGHIJKLMNOP', 'profiles': {}, - 'location': 'us-east-1'}}, + 'location': 'us-east-1'}, + 'tencentcloud': + {'id': 'new-id', + 'key': 'new-key', + 'location': 'ap-beijing', + 'profiles': {}, + 'driver': 'tencentcloud'}}, 'my-dev-envs': {'linode': {'apikey': 'abcdefghijklmnopqrstuvwxyz', @@ -1218,7 +1243,13 @@ def test_apply_cloud_providers_config_extend_multiple(self): 'key': 'supersecretkeysupersecretkey', 'driver': 'ec2', 'id': 'ABCDEFGHIJKLMNOP', - 'location': 'ap-southeast-1'}}} + 'location': 'ap-southeast-1'}, + 'tencentcloud': + {'id': 'the-tencentcloud-id', + 'key': 'the-tencentcloud-key', + 'location': 'ap-beijing', + 'profiles': {}, + 'driver': 'tencentcloud'}}} self.assertEqual(ret, salt.config.apply_cloud_providers_config( overrides, defaults=DEFAULT)) @@ -1250,7 +1281,10 @@ def test_apply_cloud_providers_config_extends_bad_provider(self): overrides = {'my-production-envs': [{'extends': 'my-dev-envs:linode', 'location': 'us-east-1', - 'user': 'ec2-user@mycorp.com'}], + 'user': 'ec2-user@mycorp.com'}, + {'extends': 'my-dev-envs:tencentcloud', + 'location': 'ap-shanghai', + 'id': 'the-tencentcloud-id'}], 'my-dev-envs': [{'id': 'ABCDEFGHIJKLMNOP', 'user': 'user@mycorp.com', @@ -1270,7 +1304,10 @@ def test_apply_cloud_providers_config_extends_no_provider(self): overrides = {'my-production-envs': [{'extends': 'my-dev-envs', 'location': 'us-east-1', - 'user': 'ec2-user@mycorp.com'}], + 'user': 'ec2-user@mycorp.com'}, + {'extends': 'my-dev-envs:tencentcloud', + 'location': 'ap-shanghai', + 'id': 'the-tencentcloud-id'}], 'my-dev-envs': [{'id': 'ABCDEFGHIJKLMNOP', 'user': 'user@mycorp.com', @@ -1296,7 +1333,12 @@ def test_apply_cloud_providers_extends_not_in_providers(self): 'user': 'user@mycorp.com', 'location': 'ap-southeast-1', 'key': 'supersecretkeysupersecretkey', - 'driver': 'linode'}], + 'driver': 'linode'}, + {'id': 'a-tencentcloud-id', + 'key': 'a-tencentcloud-key', + 'location': 'ap-guangzhou', + 'driver': 'tencentcloud' + }], 'conf_file': PATH} self.assertRaises(SaltCloudConfigError, salt.config.apply_cloud_providers_config,