cloud.rst 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424
  1. ============================
  2. Writing Cloud Driver Modules
  3. ============================
  4. Salt Cloud runs on a module system similar to the main Salt project. The
  5. modules inside saltcloud exist in the ``salt/cloud/clouds`` directory of the
  6. salt source.
  7. There are two basic types of cloud modules. If a cloud host is supported by
  8. libcloud, then using it is the fastest route to getting a module written. The
  9. Apache Libcloud project is located at:
  10. http://libcloud.apache.org/
  11. Not every cloud host is supported by libcloud. Additionally, not every
  12. feature in a supported cloud host is necessarily supported by libcloud. In
  13. either of these cases, a module can be created which does not rely on libcloud.
  14. All Driver Modules
  15. ==================
  16. The following functions are required by all driver modules, whether or not they are
  17. based on libcloud.
  18. The __virtual__() Function
  19. --------------------------
  20. This function determines whether or not to make this cloud module available
  21. upon execution. Most often, it uses ``get_configured_provider()`` to determine
  22. if the necessary configuration has been set up. It may also check for necessary
  23. imports, to decide whether to load the module. In most cases, it will return a
  24. ``True`` or ``False`` value. If the name of the driver used does not match the
  25. filename, then that name should be returned instead of ``True``. An example of
  26. this may be seen in the Azure module:
  27. https://github.com/saltstack/salt/tree/|repo_primary_branch|/salt/cloud/clouds/msazure.py
  28. The get_configured_provider() Function
  29. --------------------------------------
  30. This function uses ``config.is_provider_configured()`` to determine whether
  31. all required information for this driver has been configured. The last value
  32. in the list of required settings should be followed by a comma.
  33. Libcloud Based Modules
  34. ======================
  35. Writing a cloud module based on libcloud has two major advantages. First of all,
  36. much of the work has already been done by the libcloud project. Second, most of
  37. the functions necessary to Salt have already been added to the Salt Cloud
  38. project.
  39. The create() Function
  40. ---------------------
  41. The most important function that does need to be manually written is the
  42. ``create()`` function. This is what is used to request a virtual machine to be
  43. created by the cloud host, wait for it to become available, and then
  44. (optionally) log in and install Salt on it.
  45. A good example to follow for writing a cloud driver module based on libcloud
  46. is the module provided for Linode:
  47. https://github.com/saltstack/salt/tree/|repo_primary_branch|/salt/cloud/clouds/linode.py
  48. The basic flow of a ``create()`` function is as follows:
  49. * Send a request to the cloud host to create a virtual machine.
  50. * Wait for the virtual machine to become available.
  51. * Generate kwargs to be used to deploy Salt.
  52. * Log into the virtual machine and deploy Salt.
  53. * Return a data structure that describes the newly-created virtual machine.
  54. At various points throughout this function, events may be fired on the Salt
  55. event bus. Four of these events, which are described below, are required. Other
  56. events may be added by the user, where appropriate.
  57. When the ``create()`` function is called, it is passed a data structure called
  58. ``vm_``. This dict contains a composite of information describing the virtual
  59. machine to be created. A dict called ``__opts__`` is also provided by Salt,
  60. which contains the options used to run Salt Cloud, as well as a set of
  61. configuration and environment variables.
  62. The first thing the ``create()`` function must do is fire an event stating that
  63. it has started the create process. This event is tagged
  64. ``salt/cloud/<vm name>/creating``. The payload contains the names of the VM,
  65. profile, and provider.
  66. A set of kwargs is then usually created, to describe the parameters required
  67. by the cloud host to request the virtual machine.
  68. An event is then fired to state that a virtual machine is about to be requested.
  69. It is tagged as ``salt/cloud/<vm name>/requesting``. The payload contains most
  70. or all of the parameters that will be sent to the cloud host. Any private
  71. information (such as passwords) should not be sent in the event.
  72. After a request is made, a set of deploy kwargs will be generated. These will
  73. be used to install Salt on the target machine. Windows options are supported
  74. at this point, and should be generated, even if the cloud host does not
  75. currently support Windows. This will save time in the future if the host
  76. does eventually decide to support Windows.
  77. An event is then fired to state that the deploy process is about to begin. This
  78. event is tagged ``salt/cloud/<vm name>/deploying``. The payload for the event
  79. will contain a set of deploy kwargs, useful for debugging purposed. Any private
  80. data, including passwords and keys (including public keys) should be stripped
  81. from the deploy kwargs before the event is fired.
  82. If any Windows options have been passed in, the
  83. ``salt.utils.cloud.deploy_windows()`` function will be called. Otherwise, it
  84. will be assumed that the target is a Linux or Unix machine, and the
  85. ``salt.utils.cloud.deploy_script()`` will be called.
  86. Both of these functions will wait for the target machine to become available,
  87. then the necessary port to log in, then a successful login that can be used to
  88. install Salt. Minion configuration and keys will then be uploaded to a temporary
  89. directory on the target by the appropriate function. On a Windows target, the
  90. Windows Minion Installer will be run in silent mode. On a Linux/Unix target, a
  91. deploy script (``bootstrap-salt.sh``, by default) will be run, which will
  92. auto-detect the operating system, and install Salt using its native package
  93. manager. These do not need to be handled by the developer in the cloud module.
  94. The ``salt.utils.cloud.validate_windows_cred()`` function has been extended to
  95. take the number of retries and retry_delay parameters in case a specific cloud
  96. host has a delay between providing the Windows credentials and the
  97. credentials being available for use. In their ``create()`` function, or as
  98. a sub-function called during the creation process, developers should use the
  99. ``win_deploy_auth_retries`` and ``win_deploy_auth_retry_delay`` parameters from
  100. the provider configuration to allow the end-user the ability to customize the
  101. number of tries and delay between tries for their particular host.
  102. After the appropriate deploy function completes, a final event is fired
  103. which describes the virtual machine that has just been created. This event is
  104. tagged ``salt/cloud/<vm name>/created``. The payload contains the names of the
  105. VM, profile, and provider.
  106. Finally, a dict (queried from the provider) which describes the new virtual
  107. machine is returned to the user. Because this data is not fired on the event
  108. bus it can, and should, return any passwords that were returned by the cloud
  109. host. In some cases (for example, Rackspace), this is the only time that
  110. the password can be queried by the user; post-creation queries may not contain
  111. password information (depending upon the host).
  112. The libcloudfuncs Functions
  113. ---------------------------
  114. A number of other functions are required for all cloud hosts. However, with
  115. libcloud-based modules, these are all provided for free by the libcloudfuncs
  116. library. The following two lines set up the imports:
  117. .. code-block:: python
  118. from salt.cloud.libcloudfuncs import * # pylint: disable=W0614,W0401
  119. import salt.utils.functools
  120. And then a series of declarations will make the necessary functions available
  121. within the cloud module.
  122. .. code-block:: python
  123. get_size = salt.utils.functools.namespaced_function(get_size, globals())
  124. get_image = salt.utils.functools.namespaced_function(get_image, globals())
  125. avail_locations = salt.utils.functools.namespaced_function(avail_locations, globals())
  126. avail_images = salt.utils.functools.namespaced_function(avail_images, globals())
  127. avail_sizes = salt.utils.functools.namespaced_function(avail_sizes, globals())
  128. script = salt.utils.functools.namespaced_function(script, globals())
  129. destroy = salt.utils.functools.namespaced_function(destroy, globals())
  130. list_nodes = salt.utils.functools.namespaced_function(list_nodes, globals())
  131. list_nodes_full = salt.utils.functools.namespaced_function(list_nodes_full, globals())
  132. list_nodes_select = salt.utils.functools.namespaced_function(
  133. list_nodes_select, globals()
  134. )
  135. show_instance = salt.utils.functools.namespaced_function(show_instance, globals())
  136. If necessary, these functions may be replaced by removing the appropriate
  137. declaration line, and then adding the function as normal.
  138. These functions are required for all cloud modules, and are described in detail
  139. in the next section.
  140. Non-Libcloud Based Modules
  141. ==========================
  142. In some cases, using libcloud is not an option. This may be because libcloud has
  143. not yet included the necessary driver itself, or it may be that the driver that
  144. is included with libcloud does not contain all of the necessary features
  145. required by the developer. When this is the case, some or all of the functions
  146. in ``libcloudfuncs`` may be replaced. If they are all replaced, the libcloud
  147. imports should be absent from the Salt Cloud module.
  148. A good example of a non-libcloud driver is the DigitalOcean driver:
  149. https://github.com/saltstack/salt/tree/|repo_primary_branch|/salt/cloud/clouds/digitalocean.py
  150. The ``create()`` Function
  151. -------------------------
  152. The ``create()`` function must be created as described in the libcloud-based
  153. module documentation.
  154. The get_size() Function
  155. -----------------------
  156. This function is only necessary for libcloud-based modules, and does not need
  157. to exist otherwise.
  158. The get_image() Function
  159. ------------------------
  160. This function is only necessary for libcloud-based modules, and does not need
  161. to exist otherwise.
  162. The avail_locations() Function
  163. ------------------------------
  164. This function returns a list of locations available, if the cloud host uses
  165. multiple data centers. It is not necessary if the cloud host uses only one
  166. data center. It is normally called using the ``--list-locations`` option.
  167. .. code-block:: bash
  168. salt-cloud --list-locations my-cloud-provider
  169. The avail_images() Function
  170. ---------------------------
  171. This function returns a list of images available for this cloud provider. There
  172. are not currently any known cloud providers that do not provide this
  173. functionality, though they may refer to images by a different name (for example,
  174. "templates"). It is normally called using the ``--list-images`` option.
  175. .. code-block:: bash
  176. salt-cloud --list-images my-cloud-provider
  177. The avail_sizes() Function
  178. --------------------------
  179. This function returns a list of sizes available for this cloud provider.
  180. Generally, this refers to a combination of RAM, CPU, and/or disk space. This
  181. functionality may not be present on some cloud providers. For example, the
  182. Parallels module breaks down RAM, CPU, and disk space into separate options,
  183. whereas in other providers, these options are baked into the image. It is
  184. normally called using the ``--list-sizes`` option.
  185. .. code-block:: bash
  186. salt-cloud --list-sizes my-cloud-provider
  187. The script() Function
  188. ---------------------
  189. This function builds the deploy script to be used on the remote machine. It is
  190. likely to be moved into the ``salt.utils.cloud`` library in the near future, as
  191. it is very generic and can usually be copied wholesale from another module. An
  192. excellent example is in the Azure driver.
  193. The destroy() Function
  194. ----------------------
  195. This function irreversibly destroys a virtual machine on the cloud provider.
  196. Before doing so, it should fire an event on the Salt event bus. The tag for this
  197. event is ``salt/cloud/<vm name>/destroying``. Once the virtual machine has been
  198. destroyed, another event is fired. The tag for that event is
  199. ``salt/cloud/<vm name>/destroyed``.
  200. This function is normally called with the ``-d`` options:
  201. .. code-block:: bash
  202. salt-cloud -d myinstance
  203. The list_nodes() Function
  204. -------------------------
  205. This function returns a list of nodes available on this cloud provider, using
  206. the following fields:
  207. * id (str)
  208. * image (str)
  209. * size (str)
  210. * state (str)
  211. * private_ips (list)
  212. * public_ips (list)
  213. No other fields should be returned in this function, and all of these fields
  214. should be returned, even if empty. The private_ips and public_ips fields should
  215. always be of a list type, even if empty, and the other fields should always be
  216. of a str type. This function is normally called with the ``-Q`` option:
  217. .. code-block:: bash
  218. salt-cloud -Q
  219. The list_nodes_full() Function
  220. ------------------------------
  221. All information available about all nodes should be returned in this function.
  222. The fields in the list_nodes() function should also be returned, even if they
  223. would not normally be provided by the cloud provider. This is because some
  224. functions both within Salt and 3rd party will break if an expected field is not
  225. present. This function is normally called with the ``-F`` option:
  226. .. code-block:: bash
  227. salt-cloud -F
  228. The list_nodes_select() Function
  229. --------------------------------
  230. This function returns only the fields specified in the ``query.selection``
  231. option in ``/etc/salt/cloud``. Because this function is so generic, all of the
  232. heavy lifting has been moved into the ``salt.utils.cloud`` library.
  233. A function to call ``list_nodes_select()`` still needs to be present. In
  234. general, the following code can be used as-is:
  235. .. code-block:: python
  236. def list_nodes_select(call=None):
  237. """
  238. Return a list of the VMs that are on the provider, with select fields
  239. """
  240. return salt.utils.cloud.list_nodes_select(
  241. list_nodes_full("function"), __opts__["query.selection"], call,
  242. )
  243. However, depending on the cloud provider, additional variables may be required.
  244. For instance, some modules use a ``conn`` object, or may need to pass other
  245. options into ``list_nodes_full()``. In this case, be sure to update the function
  246. appropriately:
  247. .. code-block:: python
  248. def list_nodes_select(conn=None, call=None):
  249. """
  250. Return a list of the VMs that are on the provider, with select fields
  251. """
  252. if not conn:
  253. conn = get_conn() # pylint: disable=E0602
  254. return salt.utils.cloud.list_nodes_select(
  255. list_nodes_full(conn, "function"), __opts__["query.selection"], call,
  256. )
  257. This function is normally called with the ``-S`` option:
  258. .. code-block:: bash
  259. salt-cloud -S
  260. The show_instance() Function
  261. ----------------------------
  262. This function is used to display all of the information about a single node
  263. that is available from the cloud provider. The simplest way to provide this is
  264. usually to call ``list_nodes_full()``, and return just the data for the
  265. requested node. It is normally called as an action:
  266. .. code-block:: bash
  267. salt-cloud -a show_instance myinstance
  268. Actions and Functions
  269. =====================
  270. Extra functionality may be added to a cloud provider in the form of an
  271. ``--action`` or a ``--function``. Actions are performed against a cloud
  272. instance/virtual machine, and functions are performed against a cloud provider.
  273. Actions
  274. -------
  275. Actions are calls that are performed against a specific instance or virtual
  276. machine. The ``show_instance`` action should be available in all cloud modules.
  277. Actions are normally called with the ``-a`` option:
  278. .. code-block:: bash
  279. salt-cloud -a show_instance myinstance
  280. Actions must accept a ``name`` as a first argument, may optionally support any
  281. number of kwargs as appropriate, and must accept an argument of ``call``, with
  282. a default of ``None``.
  283. Before performing any other work, an action should normally verify that it has
  284. been called correctly. It may then perform the desired feature, and return
  285. useful information to the user. A basic action looks like:
  286. .. code-block:: python
  287. def show_instance(name, call=None):
  288. """
  289. Show the details from EC2 concerning an AMI
  290. """
  291. if call != "action":
  292. raise SaltCloudSystemExit(
  293. "The show_instance action must be called with -a or --action."
  294. )
  295. return _get_node(name)
  296. Please note that generic kwargs, if used, are passed through to actions as
  297. ``kwargs`` and not ``**kwargs``. An example of this is seen in the Functions
  298. section.
  299. Functions
  300. ---------
  301. Functions are called that are performed against a specific cloud provider. An
  302. optional function that is often useful is ``show_image``, which describes an
  303. image in detail. Functions are normally called with the ``-f`` option:
  304. .. code-block:: bash
  305. salt-cloud -f show_image my-cloud-provider image='Ubuntu 13.10 64-bit'
  306. A function may accept any number of kwargs as appropriate, and must accept an
  307. argument of ``call`` with a default of ``None``.
  308. Before performing any other work, a function should normally verify that it has
  309. been called correctly. It may then perform the desired feature, and return
  310. useful information to the user. A basic function looks like:
  311. .. code-block:: python
  312. def show_image(kwargs, call=None):
  313. """
  314. Show the details from EC2 concerning an AMI
  315. """
  316. if call != "function":
  317. raise SaltCloudSystemExit(
  318. "The show_image action must be called with -f or --function."
  319. )
  320. params = {"ImageId.1": kwargs["image"], "Action": "DescribeImages"}
  321. result = query(params)
  322. log.info(result)
  323. return result
  324. Take note that generic kwargs are passed through to functions as ``kwargs`` and
  325. not ``**kwargs``.