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..70d91dcb1193 100644 --- a/conf/cloud.providers +++ b/conf/cloud.providers @@ -87,3 +87,12 @@ #my-scaleway-config: # driver: scaleway + +#my-tencentcloud-config: + # driver: tencentcloud + # Tencent Cloud Secret Id + # id: AKIDA64pOio9BMemkApzevX0HS169S4b750A + # Tencent Cloud Secret Key + # key: 8r2xmPn0C5FDvRAlmcJimiTZKVRsk260 + # Tencent Cloud 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..0b57ddebba56 --- /dev/null +++ b/conf/cloud.providers.d/tencent.conf @@ -0,0 +1,8 @@ +#my-tencentcloud-config: + # driver: tencentcloud + # Tencent Cloud Secret Id + # id: AKIDA64pOio9BMemkApzevX0HS169S4b750A + # Tencent Cloud Secret Key + # key: 8r2xmPn0C5FDvRAlmcJimiTZKVRsk260 + # Tencent Cloud Region + # location: ap-guangzhou diff --git a/doc/ref/clouds/all/index.rst b/doc/ref/clouds/all/index.rst index 70b20e073f83..1f7a97bf02c1 100644 --- a/doc/ref/clouds/all/index.rst +++ b/doc/ref/clouds/all/index.rst @@ -34,6 +34,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/topics/cloud/features.rst b/doc/topics/cloud/features.rst index f270198deedf..9f5b736766b0 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|Tencent| + | |(Legacy)| |Ocean | | | | | | |(Legacy) | | | |Hardware | |Cloud | + +=======================+========+==========+=======+===+======+======+======+=========+=========+=========+=======+=======+=========+=========+======+=======+ + |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|Tencent| + | |(Legacy)| |Ocean | | | | | | |(Legacy) | Vagrant| |Hardware | |Cloud | + +=======================+========+==========+=======+===+======+======+======+=========+=========+=========+========+=========+=========+======+=======+ + |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|Tencent| + | |(Legacy)| |Ocean | | | | | | |(Legacy) | Vagrant| |Hardware | |Cloud | + +=======================+========+==========+=======+===+======+======+======+=========+=========+=========+========+=========+=========+======+=======+ + |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 bf2758c941f5..ce0bd82232a7 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 Tencent Cloud 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..e39d4597110e --- /dev/null +++ b/doc/topics/cloud/tencentcloud.rst @@ -0,0 +1,309 @@ +================================== +Getting Started With Tencent Cloud +================================== + +Tencent Cloud is a secure, reliable and high-performance cloud compute service +provided by Tencent. It is the 2nd largest Cloud Provider in China. + + +Dependencies +============ +The Tencent Cloud 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 + # Tencent Cloud Secret Id + id: AKIDA64pOio9BMemkApzevX0HS169S4b750A + # Tencent Cloud Secret Key + key: 8r2xmPn0C5FDvRAlmcJimiTZKVRsk260 + # Tencent Cloud Region + location: ap-guangzhou + +Configuration Parameters +~~~~~~~~~~~~~~~~~~~~~~~~ + +driver +------ +**Required**. ``tencentcloud`` to use this module. + +id +-- +**Required**. Your Tencent Cloud secret id. + +key +--- +**Required**. Your Tencent Cloud secret key. + +location +-------- +**Optional**. If this value is not specified, the default is ``ap-guangzhou``. +Available locations can be found using the ``--list-locations`` option: + +.. code-block:: bash + + # salt-cloud --list-location my-tencentcloud-config + + +Profile Configuration +===================== + +Tencent Cloud 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 availability zone that the instance is located in. +Available zones 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. +Available images 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. +Available security group ids can be found using the ``list_securitygroups`` function: + +.. code-block:: bash + + # salt-cloud -f list_securitygroups my-tencentcloud-config + +Multiple security groups are supported: + +.. code-block:: yaml + + tencentcloud-guangzhou-s1sm1: + securitygroups: + - sg-5e90804b + - sg-8kpynf2t + +hostname +-------- +**Optional**. The hostname of the instance. + +instance_charge_type +-------------------- +**Optional**. The charge type of the 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 reaches the end of the prepaid tenancy. +Valid values are ``NOTIFY_AND_AUTO_RENEW``, ``NOTIFY_AND_MANUAL_RENEW`` and ``DISABLE_NOTIFY_AND_MANUAL_RENEW``. + +.. note:: + + This value is only used when ``instance_charge_type`` is set to ``PREPAID``. + +instance_charge_type_prepaid_period +----------------------------------- +**Optional**. The tenancy time in months of the prepaid instance, +Valid values are ``1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 24, 36``. + +.. note:: + + This value is only used 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 bits per second). +Value range: ``[0, 100]``. If this value is not specified, the default is ``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, for example ``skey-16jig7tx``. + +password +-------- +**Optional**. Login password for the 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 this instance belongs to, defaults to ``0``. + +vpc_id +------ +**Optional**. The id of a VPC network. +If you want to create instances in a VPC network, this parameter must be set. + +subnet_id +--------- +**Optional**. The id of a VPC subnet. +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 supported by the Tencent Cloud 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 disk details about a specific instance. + +.. code-block:: bash + + $ salt-cloud -a show_disk myinstance + +destroy +~~~~~~~ +Destroy a Tencent Cloud instance. + +.. code-block:: bash + + $ salt-cloud -a destroy myinstance + +start +~~~~~ +Start a Tencent Cloud instance. + +.. code-block:: bash + + $ salt-cloud -a start myinstance + +stop +~~~~ +Stop a Tencent Cloud instance. + +.. code-block:: bash + + $ salt-cloud -a stop myinstance + +reboot +~~~~~~ +Reboot a Tencent Cloud instance. + +.. code-block:: bash + + $ salt-cloud -a reboot myinstance + + +Functions +========= +The following functions are currently supported by the Tencent Cloud Salt Cloud driver. + +list_securitygroups +~~~~~~~~~~~~~~~~~~~ +Lists all Tencent Cloud security groups in current region. + +.. code-block:: bash + + $ salt-cloud -f list_securitygroups my-tencentcloud-config + +list_availability_zones +~~~~~~~~~~~~~~~~~~~~~~~ +Lists all Tencent Cloud availability zones in current region. + +.. code-block:: bash + + $ salt-cloud -f list_availability_zones my-tencentcloud-config + +list_custom_images +~~~~~~~~~~~~~~~~~~ +Lists any custom images associated with the account. These images 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. This image 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..07031a3f41e7 --- /dev/null +++ b/salt/cloud/clouds/tencentcloud.py @@ -0,0 +1,1029 @@ +# -*- coding: utf-8 -*- + +''' +Tencent Cloud Cloud Module +============================= + +.. versionadded:: 3000 + +The Tencent Cloud Cloud Module is used to control access to the Tencent Cloud 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 + # Tencent Cloud Secret Id + id: AKIDA64pOio9BMemkApzevX0HS169S4b750A + # Tencent Cloud Secret Key + key: 8r2xmPn0C5FDvRAlmcJimiTZKVRsk260 + # Tencent Cloud 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 # pylint: disable=no-name-in-module + from tencentcloud.common.profile.client_profile import ClientProfile # pylint: disable=no-name-in-module + from tencentcloud.cvm.v20170312 import cvm_client # pylint: disable=no-name-in-module + from tencentcloud.cvm.v20170312 import models as cvm_models # pylint: disable=no-name-in-module + from tencentcloud.vpc.v20170312 import vpc_client # pylint: disable=no-name-in-module + from tencentcloud.vpc.v20170312 import models as vpc_models # pylint: disable=no-name-in-module + 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 Tencent Cloud +__virtualname__ = 'tencentcloud' + + +def __virtual__(): + ''' + Only load in this module if the Tencent Cloud 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 Tencent Cloud 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 Tencent Cloud 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 Tencent Cloud 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 Tencent Cloud 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 Tencent Cloud 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 Tencent Cloud 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 Tencent Cloud instance from a data dict. + + Tencent Cloud 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 Tencent Cloud 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 Tencent Cloud 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 Tencent Cloud 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 Tencent Cloud 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 Tencent Cloud 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 Tencent Cloud 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 Tencent Cloud 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 Tencent Cloud 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 Tencent Cloud 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 Tencent Cloud 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 Tencent Cloud 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..103be9ae883e --- /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 Tencent Cloud 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 Tencent Cloud + ''' + # 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..2352a60650e8 --- /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: False + internet_max_bandwidth_out: 1 + password: '' 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 3bc42624dd74..4c811c43976e 100644 --- a/tests/unit/test_config.py +++ b/tests/unit/test_config.py @@ -286,10 +286,10 @@ def test_sha256_is_default_for_minion(self, fpath): @with_tempfile() def test_proper_path_joining(self, fpath): - temp_config = 'root_dir: /\n'\ + temp_config = 'root_dir: /\n' \ 'key_logfile: key\n' if salt.utils.platform.is_windows(): - temp_config = 'root_dir: c:\\\n'\ + temp_config = 'root_dir: c:\\\n' \ 'key_logfile: key\n' with salt.utils.files.fopen(fpath, 'w') as fp_: fp_.write(temp_config) @@ -531,11 +531,8 @@ def test_master_file_roots_glob(self, tempdir, fpath): ) config = salt.config.master_config(fpath) base = config['file_roots']['base'] - self.assertEqual(set(base), set([ - os.path.join(tempdir, 'a'), - os.path.join(tempdir, 'b'), - os.path.join(tempdir, 'c') - ])) + self.assertEqual(set(base), + {os.path.join(tempdir, 'a'), os.path.join(tempdir, 'b'), os.path.join(tempdir, 'c')}) def test_validate_bad_file_roots(self): expected = salt.config._expand_glob_path( @@ -563,11 +560,8 @@ def test_master_pillar_roots_glob(self, tempdir, fpath): ) config = salt.config.master_config(fpath) base = config['pillar_roots']['base'] - self.assertEqual(set(base), set([ - os.path.join(tempdir, 'a'), - os.path.join(tempdir, 'b'), - os.path.join(tempdir, 'c') - ])) + self.assertEqual(set(base), + {os.path.join(tempdir, 'a'), os.path.join(tempdir, 'b'), os.path.join(tempdir, 'c')}) def test_validate_bad_pillar_roots(self): expected = salt.config._expand_glob_path( @@ -615,11 +609,8 @@ def test_minion_file_roots_glob(self, tempdir, fpath): ) config = salt.config.minion_config(fpath) base = config['file_roots']['base'] - self.assertEqual(set(base), set([ - os.path.join(tempdir, 'a'), - os.path.join(tempdir, 'b'), - os.path.join(tempdir, 'c') - ])) + self.assertEqual(set(base), + {os.path.join(tempdir, 'a'), os.path.join(tempdir, 'b'), os.path.join(tempdir, 'c')}) @with_tempfile() @with_tempdir() @@ -638,11 +629,8 @@ def test_minion_pillar_roots_glob(self, tempdir, fpath): ) config = salt.config.minion_config(fpath) base = config['pillar_roots']['base'] - self.assertEqual(set(base), set([ - os.path.join(tempdir, 'a'), - os.path.join(tempdir, 'b'), - os.path.join(tempdir, 'c') - ])) + self.assertEqual(set(base), + {os.path.join(tempdir, 'a'), os.path.join(tempdir, 'b'), os.path.join(tempdir, 'c')}) @with_tempdir() def test_minion_id_function(self, tempdir): @@ -679,9 +667,9 @@ def test_minion_id_lowercase(self, tempdir): minion_id_caching: False minion_id_lowercase: True ''')) - config = salt.config.minion_config(minion_config) # Load the configuration - self.assertEqual(config['minion_id_caching'], False) # Check the configuration - self.assertEqual(config['minion_id_lowercase'], True) # Check the configuration + config = salt.config.minion_config(minion_config) # Load the configuration + self.assertEqual(config['minion_id_caching'], False) # Check the configuration + self.assertEqual(config['minion_id_lowercase'], True) # Check the configuration self.assertEqual(config['id'], 'king_bob') @with_tempdir() @@ -884,7 +872,7 @@ def test_conf_file_strings_are_unicode_for_minion(self): self.assertEqual(len(non_unicode), 0, non_unicode) self.assertTrue(tally['unicode'] > 0) -# <---- Salt Cloud Configuration Tests --------------------------------------------- + # <---- Salt Cloud Configuration Tests --------------------------------------------- # cloud_config tests @@ -983,14 +971,17 @@ def test_apply_cloud_config_success_list(self): Tests success when valid data is passed into the function as a list ''' with patch('salt.config.old_to_new', - MagicMock(return_value={'default_include': 'path/to/some/cloud/conf/file', - 'providers': { - 'foo': { - 'bar': { - 'driver': 'foo:bar'}}}})): - overrides = {'providers': {'foo': [{'driver': 'bar'}]}} - ret = {'default_include': 'path/to/some/cloud/conf/file', - 'providers': {'foo': {'bar': {'driver': 'foo:bar'}}}} + MagicMock(return_value={ + 'default_include': 'path/to/some/cloud/conf/file', + 'providers': {'foo': {'bar': {'driver': 'foo:bar'}}} + })): + overrides = { + 'providers': {'foo': [{'driver': 'bar'}]} + } + ret = { + 'default_include': 'path/to/some/cloud/conf/file', + 'providers': {'foo': {'bar': {'driver': 'foo:bar'}}} + } self.assertEqual(salt.config.apply_cloud_config(overrides, defaults=DEFAULT), ret) def test_apply_cloud_config_success_dict(self): @@ -998,14 +989,16 @@ def test_apply_cloud_config_success_dict(self): Tests success when valid data is passed into function as a dictionary ''' with patch('salt.config.old_to_new', - MagicMock(return_value={'default_include': 'path/to/some/cloud/conf/file', - 'providers': { - 'foo': { - 'bar': { - 'driver': 'foo:bar'}}}})): - overrides = {'providers': {'foo': {'driver': 'bar'}}} - ret = {'default_include': 'path/to/some/cloud/conf/file', - 'providers': {'foo': {'bar': {'driver': 'foo:bar'}}}} + MagicMock(return_value={ + 'default_include': 'path/to/some/cloud/conf/file', + 'providers': {'foo': {'bar': {'driver': 'foo:bar'}}} + })): + overrides = { + 'providers': {'foo': {'driver': 'bar'}}} + ret = { + 'default_include': 'path/to/some/cloud/conf/file', + 'providers': {'foo': {'bar': {'driver': 'foo:bar'}}} + } self.assertEqual(salt.config.apply_cloud_config(overrides, defaults=DEFAULT), ret) # apply_vm_profiles_config tests @@ -1022,19 +1015,29 @@ def test_apply_vm_profiles_config_success(self): ''' Tests passing in valid provider and profile config files successfully ''' - providers = {'test-provider': - {'digitalocean': - {'driver': 'digitalocean', 'profiles': {}}}} - overrides = {'test-profile': - {'provider': 'test-provider', - 'image': 'Ubuntu 12.10 x64', - 'size': '512MB'}, - 'conf_file': PATH} - ret = {'test-profile': - {'profile': 'test-profile', - 'provider': 'test-provider:digitalocean', - 'image': 'Ubuntu 12.10 x64', - 'size': '512MB'}} + providers = { + 'test-provider': { + 'digitalocean': { + 'driver': 'digitalocean', 'profiles': {} + } + } + } + overrides = { + 'test-profile': { + 'provider': 'test-provider', + 'image': 'Ubuntu 12.10 x64', + 'size': '512MB' + }, + 'conf_file': PATH + } + ret = { + 'test-profile': { + 'profile': 'test-profile', + 'provider': 'test-provider:digitalocean', + 'image': 'Ubuntu 12.10 x64', + 'size': '512MB' + } + } self.assertEqual(salt.config.apply_vm_profiles_config(providers, overrides, defaults=DEFAULT), ret) @@ -1043,25 +1046,48 @@ def test_apply_vm_profiles_config_extend_success(self): ''' Tests profile extends functionality with valid provider and profile configs ''' - providers = {'test-config': {'ec2': {'profiles': {}, 'driver': 'ec2'}}} - overrides = {'Amazon': {'image': 'test-image-1', - 'extends': 'dev-instances'}, - 'Fedora': {'image': 'test-image-2', - 'extends': 'dev-instances'}, - 'conf_file': PATH, - 'dev-instances': {'ssh_username': 'test_user', - 'provider': 'test-config'}} - ret = {'Amazon': {'profile': 'Amazon', - 'ssh_username': 'test_user', - 'image': 'test-image-1', - 'provider': 'test-config:ec2'}, - 'Fedora': {'profile': 'Fedora', - 'ssh_username': 'test_user', - 'image': 'test-image-2', - 'provider': 'test-config:ec2'}, - 'dev-instances': {'profile': 'dev-instances', - 'ssh_username': 'test_user', - 'provider': 'test-config:ec2'}} + providers = { + 'test-config': { + 'ec2': { + 'profiles': {}, + 'driver': 'ec2' + } + } + } + overrides = { + 'Amazon': { + 'image': 'test-image-1', + 'extends': 'dev-instances' + }, + 'Fedora': { + 'image': 'test-image-2', + 'extends': 'dev-instances' + }, + 'conf_file': PATH, + 'dev-instances': { + 'ssh_username': 'test_user', + 'provider': 'test-config' + } + } + ret = { + 'Amazon': { + 'profile': 'Amazon', + 'ssh_username': 'test_user', + 'image': 'test-image-1', + 'provider': 'test-config:ec2' + }, + 'Fedora': { + 'profile': 'Fedora', + 'ssh_username': 'test_user', + 'image': 'test-image-2', + 'provider': 'test-config:ec2' + }, + 'dev-instances': { + 'profile': 'dev-instances', + 'ssh_username': 'test_user', + 'provider': 'test-config:ec2' + } + } self.assertEqual(salt.config.apply_vm_profiles_config(providers, overrides, defaults=DEFAULT), ret) @@ -1071,24 +1097,58 @@ def test_apply_vm_profiles_config_extend_override_success(self): Tests profile extends and recursively merges data elements ''' self.maxDiff = None - providers = {'test-config': {'ec2': {'profiles': {}, 'driver': 'ec2'}}} - overrides = {'Fedora': {'image': 'test-image-2', - 'extends': 'dev-instances', - 'minion': {'grains': {'stage': 'experimental'}}}, - 'conf_file': PATH, - 'dev-instances': {'ssh_username': 'test_user', - 'provider': 'test-config', - 'minion': {'grains': {'role': 'webserver'}}}} - ret = {'Fedora': {'profile': 'Fedora', - 'ssh_username': 'test_user', - 'image': 'test-image-2', - 'minion': {'grains': {'role': 'webserver', - 'stage': 'experimental'}}, - 'provider': 'test-config:ec2'}, - 'dev-instances': {'profile': 'dev-instances', - 'ssh_username': 'test_user', - 'minion': {'grains': {'role': 'webserver'}}, - 'provider': 'test-config:ec2'}} + providers = { + 'test-config': { + 'ec2': { + 'profiles': {}, + 'driver': 'ec2' + } + } + } + overrides = { + 'Fedora': { + 'image': 'test-image-2', + 'extends': 'dev-instances', + 'minion': { + 'grains': { + 'stage': 'experimental' + } + } + }, + 'conf_file': PATH, + 'dev-instances': { + 'ssh_username': 'test_user', + 'provider': 'test-config', + 'minion': { + 'grains': { + 'role': 'webserver'} + } + } + } + ret = { + 'Fedora': { + 'profile': 'Fedora', + 'ssh_username': 'test_user', + 'image': 'test-image-2', + 'minion': { + 'grains': { + 'role': 'webserver', + 'stage': 'experimental' + } + }, + 'provider': 'test-config:ec2' + }, + 'dev-instances': { + 'profile': 'dev-instances', + 'ssh_username': 'test_user', + 'minion': { + 'grains': { + 'role': 'webserver' + } + }, + 'provider': 'test-config:ec2' + } + } self.assertEqual(salt.config.apply_vm_profiles_config(providers, overrides, defaults=DEFAULT), ret) @@ -1099,14 +1159,17 @@ def test_apply_cloud_providers_config_same_providers(self): ''' Tests when two providers are given with the same provider name ''' - overrides = {'my-dev-envs': - [{'id': 'ABCDEFGHIJKLMNOP', - 'key': 'supersecretkeysupersecretkey', - 'driver': 'ec2'}, - {'apikey': 'abcdefghijklmnopqrstuvwxyz', - 'password': 'supersecret', - 'driver': 'ec2'}], - 'conf_file': PATH} + overrides = { + 'my-dev-envs': [{ + 'id': 'ABCDEFGHIJKLMNOP', + 'key': 'supersecretkeysupersecretkey', + 'driver': 'ec2' + }, { + 'apikey': 'abcdefghijklmnopqrstuvwxyz', + 'password': 'supersecret', + 'driver': 'ec2' + }], + 'conf_file': PATH} self.assertRaises(SaltCloudConfigError, salt.config.apply_cloud_providers_config, overrides, @@ -1116,44 +1179,66 @@ def test_apply_cloud_providers_config_extend(self): ''' Tests the successful extension of a cloud provider ''' - overrides = {'my-production-envs': - [{'extends': 'my-dev-envs:ec2', - 'location': 'us-east-1', - 'user': 'ec2-user@mycorp.com' - }], - 'my-dev-envs': - [{'id': 'ABCDEFGHIJKLMNOP', - 'user': 'user@mycorp.com', - 'location': 'ap-southeast-1', - 'key': 'supersecretkeysupersecretkey', - 'driver': 'ec2' - }, - {'apikey': 'abcdefghijklmnopqrstuvwxyz', - 'password': 'supersecret', - 'driver': 'linode' - }], - 'conf_file': PATH} - ret = {'my-production-envs': - {'ec2': - {'profiles': {}, - 'location': 'us-east-1', - 'key': 'supersecretkeysupersecretkey', - 'driver': 'ec2', - 'id': 'ABCDEFGHIJKLMNOP', - 'user': 'ec2-user@mycorp.com'}}, - 'my-dev-envs': - {'linode': - {'apikey': 'abcdefghijklmnopqrstuvwxyz', - 'password': 'supersecret', - 'profiles': {}, - 'driver': 'linode'}, - 'ec2': - {'profiles': {}, - 'location': 'ap-southeast-1', - 'key': 'supersecretkeysupersecretkey', - 'driver': 'ec2', - 'id': 'ABCDEFGHIJKLMNOP', - 'user': 'user@mycorp.com'}}} + overrides = { + 'my-production-envs': [{ + 'extends': 'my-dev-envs:ec2', + 'location': 'us-east-1', + 'user': 'ec2-user@mycorp.com' + }], + 'my-dev-envs': [{ + 'id': 'ABCDEFGHIJKLMNOP', + 'user': 'user@mycorp.com', + 'location': 'ap-southeast-1', + 'key': 'supersecretkeysupersecretkey', + 'driver': 'ec2' + }, { + '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': { + 'ec2': { + 'profiles': {}, + 'location': 'us-east-1', + 'key': 'supersecretkeysupersecretkey', + 'driver': 'ec2', + 'id': 'ABCDEFGHIJKLMNOP', + 'user': 'ec2-user@mycorp.com' + } + }, + 'my-dev-envs': { + 'linode': { + 'apikey': 'abcdefghijklmnopqrstuvwxyz', + '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', + 'key': 'supersecretkeysupersecretkey', + 'driver': 'ec2', + 'id': 'ABCDEFGHIJKLMNOP', + 'user': 'user@mycorp.com', + }, + }, + } self.assertEqual(ret, salt.config.apply_cloud_providers_config( overrides, @@ -1163,70 +1248,109 @@ def test_apply_cloud_providers_config_extend_multiple(self): ''' Tests the successful extension of two cloud providers ''' - overrides = {'my-production-envs': - [{'extends': 'my-dev-envs:ec2', - 'location': 'us-east-1', - 'user': 'ec2-user@mycorp.com'}, - {'password': 'new-password', - 'extends': 'my-dev-envs:linode', - 'location': 'Salt Lake City' - }], - 'my-dev-envs': - [{'id': 'ABCDEFGHIJKLMNOP', - 'user': 'user@mycorp.com', - 'location': 'ap-southeast-1', - 'key': 'supersecretkeysupersecretkey', - 'driver': 'ec2'}, - {'apikey': 'abcdefghijklmnopqrstuvwxyz', - 'password': 'supersecret', - 'driver': 'linode'}], - 'conf_file': PATH} - ret = {'my-production-envs': - {'linode': - {'apikey': 'abcdefghijklmnopqrstuvwxyz', - 'profiles': {}, - 'location': 'Salt Lake City', - 'driver': 'linode', - 'password': 'new-password'}, - 'ec2': - {'user': 'ec2-user@mycorp.com', - 'key': 'supersecretkeysupersecretkey', - 'driver': 'ec2', - 'id': 'ABCDEFGHIJKLMNOP', - 'profiles': {}, - 'location': 'us-east-1'}}, - 'my-dev-envs': - {'linode': - {'apikey': 'abcdefghijklmnopqrstuvwxyz', - 'password': 'supersecret', - 'profiles': {}, - 'driver': 'linode'}, - 'ec2': - {'profiles': {}, - 'user': 'user@mycorp.com', - 'key': 'supersecretkeysupersecretkey', - 'driver': 'ec2', - 'id': 'ABCDEFGHIJKLMNOP', - 'location': 'ap-southeast-1'}}} - self.assertEqual(ret, salt.config.apply_cloud_providers_config( - overrides, - defaults=DEFAULT)) + overrides = { + 'my-production-envs': [{ + 'extends': 'my-dev-envs:ec2', + 'location': 'us-east-1', + 'user': 'ec2-user@mycorp.com' + }, { + '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', + 'location': 'ap-southeast-1', + 'key': 'supersecretkeysupersecretkey', + 'driver': 'ec2' + }, { + 'apikey': 'abcdefghijklmnopqrstuvwxyz', + 'password': 'supersecret', + 'driver': 'linode' + }, { + 'id': 'the-tencentcloud-id', + 'location': 'ap-beijing', + 'key': 'the-tencentcloud-key', + 'driver': 'tencentcloud', + }, + ], + 'conf_file': PATH} + ret = { + 'my-production-envs': { + 'linode': { + 'apikey': 'abcdefghijklmnopqrstuvwxyz', + 'profiles': {}, + 'location': 'Salt Lake City', + 'driver': 'linode', + 'password': 'new-password' + }, + 'ec2': { + 'user': 'ec2-user@mycorp.com', + 'key': 'supersecretkeysupersecretkey', + 'driver': 'ec2', + 'id': 'ABCDEFGHIJKLMNOP', + 'profiles': {}, + 'location': 'us-east-1' + }, + 'tencentcloud': { + 'id': 'new-id', + 'key': 'new-key', + 'location': 'ap-beijing', + 'profiles': {}, + 'driver': 'tencentcloud', + }, + }, + 'my-dev-envs': { + 'linode': { + 'apikey': 'abcdefghijklmnopqrstuvwxyz', + 'password': 'supersecret', + 'profiles': {}, + 'driver': 'linode' + }, + 'ec2': { + 'profiles': {}, + 'user': 'user@mycorp.com', + 'key': 'supersecretkeysupersecretkey', + 'driver': 'ec2', + 'id': 'ABCDEFGHIJKLMNOP', + '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)) def test_apply_cloud_providers_config_extends_bad_alias(self): ''' Tests when the extension contains an alias not found in providers list ''' - overrides = {'my-production-envs': - [{'extends': 'test-alias:ec2', - 'location': 'us-east-1', - 'user': 'ec2-user@mycorp.com'}], - 'my-dev-envs': - [{'id': 'ABCDEFGHIJKLMNOP', - 'user': 'user@mycorp.com', - 'location': 'ap-southeast-1', - 'key': 'supersecretkeysupersecretkey', - 'driver': 'ec2'}], - 'conf_file': PATH} + overrides = { + 'my-production-envs': [{ + 'extends': 'test-alias:ec2', + 'location': 'us-east-1', + 'user': 'ec2-user@mycorp.com' + }], + 'my-dev-envs': [{ + 'id': 'ABCDEFGHIJKLMNOP', + 'user': 'user@mycorp.com', + 'location': 'ap-southeast-1', + 'key': 'supersecretkeysupersecretkey', + 'driver': 'ec2' + }], + 'conf_file': PATH} self.assertRaises(SaltCloudConfigError, salt.config.apply_cloud_providers_config, overrides, @@ -1236,17 +1360,25 @@ def test_apply_cloud_providers_config_extends_bad_provider(self): ''' Tests when the extension contains a provider not found in providers list ''' - overrides = {'my-production-envs': - [{'extends': 'my-dev-envs:linode', - 'location': 'us-east-1', - 'user': 'ec2-user@mycorp.com'}], - 'my-dev-envs': - [{'id': 'ABCDEFGHIJKLMNOP', - 'user': 'user@mycorp.com', - 'location': 'ap-southeast-1', - 'key': 'supersecretkeysupersecretkey', - 'driver': 'ec2'}], - 'conf_file': PATH} + overrides = { + 'my-production-envs': [{ + 'extends': 'my-dev-envs:linode', + 'location': 'us-east-1', + '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', + 'location': 'ap-southeast-1', + 'key': 'supersecretkeysupersecretkey', + 'driver': 'ec2' + }], + 'conf_file': PATH} self.assertRaises(SaltCloudConfigError, salt.config.apply_cloud_providers_config, overrides, @@ -1256,17 +1388,25 @@ def test_apply_cloud_providers_config_extends_no_provider(self): ''' Tests when no provider is supplied in the extends statement ''' - overrides = {'my-production-envs': - [{'extends': 'my-dev-envs', - 'location': 'us-east-1', - 'user': 'ec2-user@mycorp.com'}], - 'my-dev-envs': - [{'id': 'ABCDEFGHIJKLMNOP', - 'user': 'user@mycorp.com', - 'location': 'ap-southeast-1', - 'key': 'supersecretkeysupersecretkey', - 'driver': 'linode'}], - 'conf_file': PATH} + overrides = { + 'my-production-envs': [{ + 'extends': 'my-dev-envs', + 'location': 'us-east-1', + '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', + 'location': 'ap-southeast-1', + 'key': 'supersecretkeysupersecretkey', + 'driver': 'linode' + }], + 'conf_file': PATH} self.assertRaises(SaltCloudConfigError, salt.config.apply_cloud_providers_config, overrides, @@ -1276,17 +1416,26 @@ def test_apply_cloud_providers_extends_not_in_providers(self): ''' Tests when extends is not in the list of providers ''' - overrides = {'my-production-envs': - [{'extends': 'my-dev-envs ec2', - 'location': 'us-east-1', - 'user': 'ec2-user@mycorp.com'}], - 'my-dev-envs': - [{'id': 'ABCDEFGHIJKLMNOP', - 'user': 'user@mycorp.com', - 'location': 'ap-southeast-1', - 'key': 'supersecretkeysupersecretkey', - 'driver': 'linode'}], - 'conf_file': PATH} + overrides = { + 'my-production-envs': [{ + 'extends': 'my-dev-envs ec2', + 'location': 'us-east-1', + 'user': 'ec2-user@mycorp.com' + }], + 'my-dev-envs': [{ + 'id': 'ABCDEFGHIJKLMNOP', + 'user': 'user@mycorp.com', + 'location': 'ap-southeast-1', + 'key': 'supersecretkeysupersecretkey', + '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, overrides, @@ -1442,7 +1591,7 @@ def test_includes_load(self): self.assertIn('ec2-config', config['providers']) self.assertIn('ec2-test', config['profiles']) -# <---- Salt Cloud Configuration Tests --------------------------------------------- + # <---- Salt Cloud Configuration Tests --------------------------------------------- def test_include_config_without_errors(self): ''' @@ -1514,9 +1663,8 @@ def test_apply_config(self): Ensure that the environment and saltenv options work properly ''' with patch.object(salt.config, '_adjust_log_file_override', Mock()), \ - patch.object(salt.config, '_update_ssl_config', Mock()), \ - patch.object(salt.config, '_update_discovery_config', Mock()): - + patch.object(salt.config, '_update_ssl_config', Mock()), \ + patch.object(salt.config, '_update_discovery_config', Mock()): # MASTER CONFIG # Ensure that environment overrides saltenv when saltenv not @@ -1580,6 +1728,7 @@ class APIConfigTestCase(DefaultConfigsBase, TestCase): ''' TestCase for the api_config function in salt.config.__init__.py ''' + def setUp(self): # Copy DEFAULT_API_OPTS to restore after the test self.default_api_opts = salt.config.DEFAULT_API_OPTS.copy() @@ -1595,8 +1744,7 @@ def test_api_config_log_file_values(self): the DEFAULT_API_OPTS 'api_logfile' value. ''' with patch('salt.config.client_config', MagicMock(return_value=self.mock_master_default_opts)): - - expected = '{}/var/log/salt/api'.format( + expected = '{0}/var/log/salt/api'.format( RUNTIME_VARS.TMP_ROOT_DIR if RUNTIME_VARS.TMP_ROOT_DIR != '/' else '') if salt.utils.platform.is_windows(): expected = '{}\\var\\log\\salt\\api'.format( @@ -1612,8 +1760,7 @@ def test_api_config_pidfile_values(self): the DEFAULT_API_OPTS 'api_pidfile' value. ''' with patch('salt.config.client_config', MagicMock(return_value=self.mock_master_default_opts)): - - expected = '{}/var/run/salt-api.pid'.format( + expected = '{0}/var/run/salt-api.pid'.format( RUNTIME_VARS.TMP_ROOT_DIR if RUNTIME_VARS.TMP_ROOT_DIR != '/' else '') if salt.utils.platform.is_windows(): expected = '{}\\var\\run\\salt-api.pid'.format(