PageRenderTime 504ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/salt/cloud/clouds/gce.py

https://gitlab.com/ricardo.hernandez/salt
Python | 1904 lines | 1900 code | 0 blank | 4 comment | 0 complexity | d407a7fb7923cf53ee055c256b4388a0 MD5 | raw file
  1. # -*- coding: utf-8 -*-
  2. '''
  3. Copyright 2013 Google Inc. All Rights Reserved.
  4. Licensed under the Apache License, Version 2.0 (the "License");
  5. you may not use this file except in compliance with the License.
  6. You may obtain a copy of the License at
  7. http://www.apache.org/licenses/LICENSE-2.0
  8. Unless required by applicable law or agreed to in writing, software
  9. distributed under the License is distributed on an "AS IS" BASIS,
  10. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. See the License for the specific language governing permissions and
  12. limitations under the License.
  13. Google Compute Engine Module
  14. ============================
  15. The Google Compute Engine module. This module interfaces with Google Compute
  16. Engine (GCE). To authenticate to GCE, you will need to create a Service Account.
  17. To set up Service Account Authentication, follow the :ref:`gce_setup` instructions.
  18. Example Provider Configuration
  19. ------------------------------
  20. .. code-block:: yaml
  21. my-gce-config:
  22. # The Google Cloud Platform Project ID
  23. project: "my-project-id"
  24. # The Service ACcount client ID
  25. service_account_email_address: 1234567890@developer.gserviceaccount.com
  26. # The location of the private key (PEM format)
  27. service_account_private_key: /home/erjohnso/PRIVKEY.pem
  28. driver: gce
  29. # Specify whether to use public or private IP for deploy script.
  30. # Valid options are:
  31. # private_ips - The salt-master is also hosted with GCE
  32. # public_ips - The salt-master is hosted outside of GCE
  33. ssh_interface: public_ips
  34. :maintainer: Eric Johnson <erjohnso@google.com>
  35. :depends: libcloud >= 0.14.1
  36. '''
  37. # pylint: disable=invalid-name,function-redefined
  38. # Import python libs
  39. from __future__ import absolute_import
  40. import os
  41. import re
  42. import pprint
  43. import logging
  44. import msgpack
  45. from ast import literal_eval
  46. # Import 3rd-party libs
  47. # pylint: disable=import-error
  48. try:
  49. from libcloud.compute.types import Provider
  50. from libcloud.compute.providers import get_driver
  51. from libcloud.loadbalancer.types import Provider as Provider_lb
  52. from libcloud.loadbalancer.providers import get_driver as get_driver_lb
  53. from libcloud.common.google import (
  54. ResourceInUseError,
  55. ResourceNotFoundError,
  56. )
  57. # See https://github.com/saltstack/salt/issues/32743
  58. import libcloud.security
  59. libcloud.security.CA_CERTS_PATH.append('/etc/ssl/certs/YaST-CA.pem')
  60. HAS_LIBCLOUD = True
  61. except ImportError:
  62. HAS_LIBCLOUD = False
  63. # pylint: enable=import-error
  64. # Import salt libs
  65. from salt.utils import namespaced_function
  66. import salt.ext.six as six
  67. import salt.utils.cloud
  68. import salt.config as config
  69. from salt.utils import http
  70. from salt import syspaths
  71. from salt.cloud.libcloudfuncs import * # pylint: disable=redefined-builtin,wildcard-import,unused-wildcard-import
  72. from salt.exceptions import (
  73. SaltCloudSystemExit,
  74. )
  75. # Get logging started
  76. log = logging.getLogger(__name__)
  77. __virtualname__ = 'gce'
  78. # custom UA
  79. _UA_PRODUCT = 'salt-cloud'
  80. _UA_VERSION = '0.2.0'
  81. # Redirect GCE functions to this module namespace
  82. avail_locations = namespaced_function(avail_locations, globals())
  83. script = namespaced_function(script, globals())
  84. destroy = namespaced_function(destroy, globals())
  85. list_nodes = namespaced_function(list_nodes, globals())
  86. list_nodes_full = namespaced_function(list_nodes_full, globals())
  87. list_nodes_select = namespaced_function(list_nodes_select, globals())
  88. GCE_VM_NAME_REGEX = re.compile(r'^(?:[a-z](?:[-a-z0-9]{0,61}[a-z0-9])?)$')
  89. # Only load in this module if the GCE configurations are in place
  90. def __virtual__():
  91. '''
  92. Set up the libcloud functions and check for GCE configurations.
  93. '''
  94. if get_configured_provider() is False:
  95. return False
  96. if get_dependencies() is False:
  97. return False
  98. for provider, details in six.iteritems(__opts__['providers']):
  99. if 'gce' not in details:
  100. continue
  101. parameters = details['gce']
  102. pathname = os.path.expanduser(parameters['service_account_private_key'])
  103. if salt.utils.cloud.check_key_path_and_mode(
  104. provider, pathname
  105. ) is False:
  106. return False
  107. return __virtualname__
  108. def get_configured_provider():
  109. '''
  110. Return the first configured instance.
  111. '''
  112. return config.is_provider_configured(
  113. __opts__,
  114. __active_provider_name__ or 'gce',
  115. ('project',
  116. 'service_account_email_address',
  117. 'service_account_private_key')
  118. )
  119. def get_dependencies():
  120. '''
  121. Warn if dependencies aren't met.
  122. '''
  123. return config.check_driver_dependencies(
  124. __virtualname__,
  125. {'libcloud': HAS_LIBCLOUD}
  126. )
  127. def get_lb_conn(gce_driver=None):
  128. '''
  129. Return a load-balancer conn object
  130. '''
  131. if not gce_driver:
  132. raise SaltCloudSystemExit(
  133. 'Missing gce_driver for get_lb_conn method.'
  134. )
  135. return get_driver_lb(Provider_lb.GCE)(gce_driver=gce_driver)
  136. def get_conn():
  137. '''
  138. Return a conn object for the passed VM data
  139. '''
  140. driver = get_driver(Provider.GCE)
  141. provider = get_configured_provider()
  142. project = config.get_cloud_config_value('project', provider, __opts__)
  143. email = config.get_cloud_config_value('service_account_email_address',
  144. provider, __opts__)
  145. private_key = config.get_cloud_config_value('service_account_private_key',
  146. provider, __opts__)
  147. gce = driver(email, private_key, project=project)
  148. gce.connection.user_agent_append('{0}/{1}'.format(_UA_PRODUCT,
  149. _UA_VERSION))
  150. return gce
  151. def _expand_item(item):
  152. '''
  153. Convert the libcloud object into something more serializable.
  154. '''
  155. ret = {}
  156. ret.update(item.__dict__)
  157. return ret
  158. def _expand_node(node):
  159. '''
  160. Convert the libcloud Node object into something more serializable.
  161. '''
  162. ret = {}
  163. ret.update(node.__dict__)
  164. try:
  165. del ret['extra']['boot_disk']
  166. except Exception: # pylint: disable=W0703
  167. pass
  168. zone = ret['extra']['zone']
  169. ret['extra']['zone'] = {}
  170. ret['extra']['zone'].update(zone.__dict__)
  171. return ret
  172. def _expand_disk(disk):
  173. '''
  174. Convert the libcloud Volume object into something more serializable.
  175. '''
  176. ret = {}
  177. ret.update(disk.__dict__)
  178. zone = ret['extra']['zone']
  179. ret['extra']['zone'] = {}
  180. ret['extra']['zone'].update(zone.__dict__)
  181. return ret
  182. def _expand_address(addy):
  183. '''
  184. Convert the libcloud GCEAddress object into something more serializable.
  185. '''
  186. ret = {}
  187. ret.update(addy.__dict__)
  188. ret['extra']['zone'] = addy.region.name
  189. return ret
  190. def _expand_balancer(lb):
  191. '''
  192. Convert the libcloud load-balancer object into something more serializable.
  193. '''
  194. ret = {}
  195. ret.update(lb.__dict__)
  196. hc = ret['extra']['healthchecks']
  197. ret['extra']['healthchecks'] = []
  198. for item in hc:
  199. ret['extra']['healthchecks'].append(_expand_item(item))
  200. fwr = ret['extra']['forwarding_rule']
  201. tp = ret['extra']['forwarding_rule'].targetpool
  202. reg = ret['extra']['forwarding_rule'].region
  203. ret['extra']['forwarding_rule'] = {}
  204. ret['extra']['forwarding_rule'].update(fwr.__dict__)
  205. ret['extra']['forwarding_rule']['targetpool'] = tp.name
  206. ret['extra']['forwarding_rule']['region'] = reg.name
  207. tp = ret['extra']['targetpool']
  208. hc = ret['extra']['targetpool'].healthchecks
  209. nodes = ret['extra']['targetpool'].nodes
  210. region = ret['extra']['targetpool'].region
  211. zones = ret['extra']['targetpool'].region.zones
  212. ret['extra']['targetpool'] = {}
  213. ret['extra']['targetpool'].update(tp.__dict__)
  214. ret['extra']['targetpool']['region'] = _expand_item(region)
  215. ret['extra']['targetpool']['nodes'] = []
  216. for n in nodes:
  217. ret['extra']['targetpool']['nodes'].append(_expand_node(n))
  218. ret['extra']['targetpool']['healthchecks'] = []
  219. for hci in hc:
  220. ret['extra']['targetpool']['healthchecks'].append(hci.name)
  221. ret['extra']['targetpool']['region']['zones'] = []
  222. for z in zones:
  223. ret['extra']['targetpool']['region']['zones'].append(z.name)
  224. return ret
  225. def show_instance(vm_name, call=None):
  226. '''
  227. Show the details of the existing instance.
  228. '''
  229. if call != 'action':
  230. raise SaltCloudSystemExit(
  231. 'The show_instance action must be called with -a or --action.'
  232. )
  233. conn = get_conn()
  234. node = _expand_node(conn.ex_get_node(vm_name))
  235. salt.utils.cloud.cache_node(node, __active_provider_name__, __opts__)
  236. return node
  237. def avail_sizes(conn=None):
  238. '''
  239. Return a dict of available instances sizes (a.k.a machine types) and
  240. convert them to something more serializable.
  241. '''
  242. if not conn:
  243. conn = get_conn()
  244. raw_sizes = conn.list_sizes('all') # get *all* the machine types!
  245. sizes = []
  246. for size in raw_sizes:
  247. zone = size.extra['zone']
  248. size.extra['zone'] = {}
  249. size.extra['zone'].update(zone.__dict__)
  250. mtype = {}
  251. mtype.update(size.__dict__)
  252. sizes.append(mtype)
  253. return sizes
  254. def avail_images(conn=None):
  255. '''
  256. Return a dict of all available VM images on the cloud provider with
  257. relevant data.
  258. Note that for GCE, there are custom images within the project, but the
  259. generic images are in other projects. This returns a dict of images in
  260. the project plus images in well-known public projects that provide supported
  261. images, as listed on this page:
  262. https://cloud.google.com/compute/docs/operating-systems/
  263. If image names overlap, the image in the current project is used.
  264. '''
  265. if not conn:
  266. conn = get_conn()
  267. all_images = []
  268. # The list of public image projects can be found via:
  269. # % gcloud compute images list
  270. # and looking at the "PROJECT" column in the output.
  271. public_image_projects = (
  272. 'centos-cloud', 'coreos-cloud', 'debian-cloud', 'google-containers',
  273. 'opensuse-cloud', 'rhel-cloud', 'suse-cloud', 'ubuntu-os-cloud',
  274. 'windows-cloud'
  275. )
  276. for project in public_image_projects:
  277. all_images.extend(conn.list_images(project))
  278. # Finally, add the images in this current project last so that it overrides
  279. # any image that also exists in any public project.
  280. all_images.extend(conn.list_images())
  281. ret = {}
  282. for img in all_images:
  283. ret[img.name] = {}
  284. for attr in dir(img):
  285. if attr.startswith('_'):
  286. continue
  287. ret[img.name][attr] = getattr(img, attr)
  288. return ret
  289. def __get_image(conn, vm_):
  290. '''
  291. The get_image for GCE allows partial name matching and returns a
  292. libcloud object.
  293. '''
  294. img = config.get_cloud_config_value(
  295. 'image', vm_, __opts__, default='debian-7', search_global=False)
  296. return conn.ex_get_image(img)
  297. def __get_location(conn, vm_):
  298. '''
  299. Need to override libcloud to find the zone.
  300. '''
  301. location = config.get_cloud_config_value(
  302. 'location', vm_, __opts__)
  303. return conn.ex_get_zone(location)
  304. def __get_size(conn, vm_):
  305. '''
  306. Need to override libcloud to find the machine type in the proper zone.
  307. '''
  308. size = config.get_cloud_config_value(
  309. 'size', vm_, __opts__, default='n1-standard-1', search_global=False)
  310. return conn.ex_get_size(size, __get_location(conn, vm_))
  311. def __get_tags(vm_):
  312. '''
  313. Get configured tags.
  314. '''
  315. t = config.get_cloud_config_value(
  316. 'tags', vm_, __opts__,
  317. default='[]', search_global=False)
  318. # Consider warning the user that the tags in the cloud profile
  319. # could not be interpreted, bad formatting?
  320. try:
  321. tags = literal_eval(t)
  322. except Exception: # pylint: disable=W0703
  323. tags = None
  324. if not tags or not isinstance(tags, list):
  325. tags = None
  326. return tags
  327. def __get_metadata(vm_):
  328. '''
  329. Get configured metadata and add 'salt-cloud-profile'.
  330. '''
  331. md = config.get_cloud_config_value(
  332. 'metadata', vm_, __opts__,
  333. default='{}', search_global=False)
  334. # Consider warning the user that the metadata in the cloud profile
  335. # could not be interpreted, bad formatting?
  336. try:
  337. metadata = literal_eval(md)
  338. except Exception: # pylint: disable=W0703
  339. metadata = None
  340. if not metadata or not isinstance(metadata, dict):
  341. metadata = {'items': [{
  342. 'key': 'salt-cloud-profile',
  343. 'value': vm_['profile']
  344. }]}
  345. else:
  346. metadata['salt-cloud-profile'] = vm_['profile']
  347. items = []
  348. for k, v in six.iteritems(metadata):
  349. items.append({'key': k, 'value': v})
  350. metadata = {'items': items}
  351. return metadata
  352. def __get_host(node, vm_):
  353. '''
  354. Return public IP, private IP, or hostname for the libcloud 'node' object
  355. '''
  356. if __get_ssh_interface(vm_) == 'private_ips' or vm_['external_ip'] is None:
  357. ip_address = node.private_ips[0]
  358. log.info('Salt node data. Private_ip: {0}'.format(ip_address))
  359. else:
  360. ip_address = node.public_ips[0]
  361. log.info('Salt node data. Public_ip: {0}'.format(ip_address))
  362. if len(ip_address) > 0:
  363. return ip_address
  364. return node.name
  365. def __get_network(conn, vm_):
  366. '''
  367. Return a GCE libcloud network object with matching name
  368. '''
  369. network = config.get_cloud_config_value(
  370. 'network', vm_, __opts__,
  371. default='default', search_global=False)
  372. return conn.ex_get_network(network)
  373. def __get_ssh_interface(vm_):
  374. '''
  375. Return the ssh_interface type to connect to. Either 'public_ips' (default)
  376. or 'private_ips'.
  377. '''
  378. return config.get_cloud_config_value(
  379. 'ssh_interface', vm_, __opts__, default='public_ips',
  380. search_global=False
  381. )
  382. def __create_orget_address(conn, name, region):
  383. '''
  384. Reuse or create a static IP address.
  385. Returns a native GCEAddress construct to use with libcloud.
  386. '''
  387. try:
  388. addy = conn.ex_get_address(name, region)
  389. except ResourceNotFoundError: # pylint: disable=W0703
  390. addr_kwargs = {
  391. 'name': name,
  392. 'region': region
  393. }
  394. new_addy = create_address(addr_kwargs, "function")
  395. addy = conn.ex_get_address(new_addy['name'], new_addy['region'])
  396. return addy
  397. def _parse_allow(allow):
  398. '''
  399. Convert firewall rule allowed user-string to specified REST API format.
  400. '''
  401. # input=> tcp:53,tcp:80,tcp:443,icmp,tcp:4201,udp:53
  402. # output<= [
  403. # {"IPProtocol": "tcp", "ports": ["53","80","443","4201"]},
  404. # {"IPProtocol": "icmp"},
  405. # {"IPProtocol": "udp", "ports": ["53"]},
  406. # ]
  407. seen_protos = {}
  408. allow_dict = []
  409. protocols = allow.split(',')
  410. for p in protocols:
  411. pairs = p.split(':')
  412. if pairs[0].lower() not in ['tcp', 'udp', 'icmp']:
  413. raise SaltCloudSystemExit(
  414. 'Unsupported protocol {0}. Must be tcp, udp, or icmp.'.format(
  415. pairs[0]
  416. )
  417. )
  418. if len(pairs) == 1 or pairs[0].lower() == 'icmp':
  419. seen_protos[pairs[0]] = []
  420. else:
  421. if pairs[0] not in seen_protos:
  422. seen_protos[pairs[0]] = [pairs[1]]
  423. else:
  424. seen_protos[pairs[0]].append(pairs[1])
  425. for k in seen_protos:
  426. d = {'IPProtocol': k}
  427. if len(seen_protos[k]) > 0:
  428. d['ports'] = seen_protos[k]
  429. allow_dict.append(d)
  430. log.debug("firewall allowed protocols/ports: {0}".format(allow_dict))
  431. return allow_dict
  432. def __get_ssh_credentials(vm_):
  433. '''
  434. Get configured SSH credentials.
  435. '''
  436. ssh_user = config.get_cloud_config_value(
  437. 'ssh_username', vm_, __opts__, default=os.getenv('USER'))
  438. ssh_key = config.get_cloud_config_value(
  439. 'ssh_keyfile', vm_, __opts__,
  440. default=os.path.expanduser('~/.ssh/google_compute_engine'))
  441. return ssh_user, ssh_key
  442. def create_network(kwargs=None, call=None):
  443. '''
  444. Create a GCE network.
  445. CLI Example:
  446. .. code-block:: bash
  447. salt-cloud -f create_network gce name=mynet cidr=10.10.10.0/24
  448. '''
  449. if call != 'function':
  450. raise SaltCloudSystemExit(
  451. 'The create_network function must be called with -f or --function.'
  452. )
  453. if not kwargs or 'name' not in kwargs:
  454. log.error(
  455. 'A name must be specified when creating a network.'
  456. )
  457. return False
  458. if 'cidr' not in kwargs:
  459. log.error(
  460. 'A network CIDR range must be specified when creating a network.'
  461. )
  462. return
  463. name = kwargs['name']
  464. cidr = kwargs['cidr']
  465. conn = get_conn()
  466. salt.utils.cloud.fire_event(
  467. 'event',
  468. 'create network',
  469. 'salt/cloud/net/creating',
  470. {
  471. 'name': name,
  472. 'cidr': cidr,
  473. },
  474. transport=__opts__['transport']
  475. )
  476. network = conn.ex_create_network(name, cidr)
  477. salt.utils.cloud.fire_event(
  478. 'event',
  479. 'created network',
  480. 'salt/cloud/net/created',
  481. {
  482. 'name': name,
  483. 'cidr': cidr,
  484. },
  485. transport=__opts__['transport']
  486. )
  487. return _expand_item(network)
  488. def delete_network(kwargs=None, call=None):
  489. '''
  490. Permanently delete a network.
  491. CLI Example:
  492. .. code-block:: bash
  493. salt-cloud -f delete_network gce name=mynet
  494. '''
  495. if call != 'function':
  496. raise SaltCloudSystemExit(
  497. 'The delete_network function must be called with -f or --function.'
  498. )
  499. if not kwargs or 'name' not in kwargs:
  500. log.error(
  501. 'A name must be specified when deleting a network.'
  502. )
  503. return False
  504. name = kwargs['name']
  505. conn = get_conn()
  506. salt.utils.cloud.fire_event(
  507. 'event',
  508. 'delete network',
  509. 'salt/cloud/net/deleting',
  510. {
  511. 'name': name,
  512. },
  513. transport=__opts__['transport']
  514. )
  515. try:
  516. result = conn.ex_destroy_network(
  517. conn.ex_get_network(name)
  518. )
  519. except ResourceNotFoundError as exc:
  520. log.error(
  521. 'Nework {0} could not be found.\n'
  522. 'The following exception was thrown by libcloud:\n{1}'.format(
  523. name, exc),
  524. exc_info_on_loglevel=logging.DEBUG
  525. )
  526. return False
  527. salt.utils.cloud.fire_event(
  528. 'event',
  529. 'deleted network',
  530. 'salt/cloud/net/deleted',
  531. {
  532. 'name': name,
  533. },
  534. transport=__opts__['transport']
  535. )
  536. return result
  537. def show_network(kwargs=None, call=None):
  538. '''
  539. Show the details of an existing network.
  540. CLI Example:
  541. .. code-block:: bash
  542. salt-cloud -f show_network gce name=mynet
  543. '''
  544. if call != 'function':
  545. raise SaltCloudSystemExit(
  546. 'The show_network function must be called with -f or --function.'
  547. )
  548. if not kwargs or 'name' not in kwargs:
  549. log.error(
  550. 'Must specify name of network.'
  551. )
  552. return False
  553. conn = get_conn()
  554. return _expand_item(conn.ex_get_network(kwargs['name']))
  555. def create_fwrule(kwargs=None, call=None):
  556. '''
  557. Create a GCE firewall rule. The 'default' network is used if not specified.
  558. CLI Example:
  559. .. code-block:: bash
  560. salt-cloud -f create_fwrule gce name=allow-http allow=tcp:80
  561. '''
  562. if call != 'function':
  563. raise SaltCloudSystemExit(
  564. 'The create_fwrule function must be called with -f or --function.'
  565. )
  566. if not kwargs or 'name' not in kwargs:
  567. log.error(
  568. 'A name must be specified when creating a firewall rule.'
  569. )
  570. return False
  571. if 'allow' not in kwargs:
  572. log.error(
  573. 'Must use "allow" to specify allowed protocols/ports.'
  574. )
  575. return False
  576. name = kwargs['name']
  577. network_name = kwargs.get('network', 'default')
  578. allow = _parse_allow(kwargs['allow'])
  579. src_range = kwargs.get('src_range', '0.0.0.0/0')
  580. src_tags = kwargs.get('src_tags', None)
  581. dst_tags = kwargs.get('dst_tags', None)
  582. if src_range:
  583. src_range = src_range.split(',')
  584. if src_tags:
  585. src_tags = src_tags.split(',')
  586. if dst_tags:
  587. dst_tags = dst_tags.split(',')
  588. conn = get_conn()
  589. salt.utils.cloud.fire_event(
  590. 'event',
  591. 'create firewall',
  592. 'salt/cloud/firewall/creating',
  593. {
  594. 'name': name,
  595. 'network': network_name,
  596. 'allow': kwargs['allow'],
  597. },
  598. transport=__opts__['transport']
  599. )
  600. fwrule = conn.ex_create_firewall(
  601. name, allow,
  602. network=network_name,
  603. source_ranges=src_range,
  604. source_tags=src_tags,
  605. target_tags=dst_tags
  606. )
  607. salt.utils.cloud.fire_event(
  608. 'event',
  609. 'created firewall',
  610. 'salt/cloud/firewall/created',
  611. {
  612. 'name': name,
  613. 'network': network_name,
  614. 'allow': kwargs['allow'],
  615. },
  616. transport=__opts__['transport']
  617. )
  618. return _expand_item(fwrule)
  619. def delete_fwrule(kwargs=None, call=None):
  620. '''
  621. Permanently delete a firewall rule.
  622. CLI Example:
  623. .. code-block:: bash
  624. salt-cloud -f delete_fwrule gce name=allow-http
  625. '''
  626. if call != 'function':
  627. raise SaltCloudSystemExit(
  628. 'The delete_fwrule function must be called with -f or --function.'
  629. )
  630. if not kwargs or 'name' not in kwargs:
  631. log.error(
  632. 'A name must be specified when deleting a firewall rule.'
  633. )
  634. return False
  635. name = kwargs['name']
  636. conn = get_conn()
  637. salt.utils.cloud.fire_event(
  638. 'event',
  639. 'delete firewall',
  640. 'salt/cloud/firewall/deleting',
  641. {
  642. 'name': name,
  643. },
  644. transport=__opts__['transport']
  645. )
  646. try:
  647. result = conn.ex_destroy_firewall(
  648. conn.ex_get_firewall(name)
  649. )
  650. except ResourceNotFoundError as exc:
  651. log.error(
  652. 'Rule {0} could not be found.\n'
  653. 'The following exception was thrown by libcloud:\n{1}'.format(
  654. name, exc),
  655. exc_info_on_loglevel=logging.DEBUG
  656. )
  657. return False
  658. salt.utils.cloud.fire_event(
  659. 'event',
  660. 'deleted firewall',
  661. 'salt/cloud/firewall/deleted',
  662. {
  663. 'name': name,
  664. },
  665. transport=__opts__['transport']
  666. )
  667. return result
  668. def show_fwrule(kwargs=None, call=None):
  669. '''
  670. Show the details of an existing firewall rule.
  671. CLI Example:
  672. .. code-block:: bash
  673. salt-cloud -f show_fwrule gce name=allow-http
  674. '''
  675. if call != 'function':
  676. raise SaltCloudSystemExit(
  677. 'The show_fwrule function must be called with -f or --function.'
  678. )
  679. if not kwargs or 'name' not in kwargs:
  680. log.error(
  681. 'Must specify name of network.'
  682. )
  683. return False
  684. conn = get_conn()
  685. return _expand_item(conn.ex_get_firewall(kwargs['name']))
  686. def create_hc(kwargs=None, call=None):
  687. '''
  688. Create an HTTP health check configuration.
  689. CLI Example:
  690. .. code-block:: bash
  691. salt-cloud -f create_hc gce name=hc path=/healthy port=80
  692. '''
  693. if call != 'function':
  694. raise SaltCloudSystemExit(
  695. 'The create_hc function must be called with -f or --function.'
  696. )
  697. if not kwargs or 'name' not in kwargs:
  698. log.error(
  699. 'A name must be specified when creating a health check.'
  700. )
  701. return False
  702. name = kwargs['name']
  703. host = kwargs.get('host', None)
  704. path = kwargs.get('path', None)
  705. port = kwargs.get('port', None)
  706. interval = kwargs.get('interval', None)
  707. timeout = kwargs.get('timeout', None)
  708. unhealthy_threshold = kwargs.get('unhealthy_threshold', None)
  709. healthy_threshold = kwargs.get('healthy_threshold', None)
  710. conn = get_conn()
  711. salt.utils.cloud.fire_event(
  712. 'event',
  713. 'create health_check',
  714. 'salt/cloud/healthcheck/creating',
  715. {
  716. 'name': name,
  717. 'host': host,
  718. 'path': path,
  719. 'port': port,
  720. 'interval': interval,
  721. 'timeout': timeout,
  722. 'unhealthy_threshold': unhealthy_threshold,
  723. 'healthy_threshold': healthy_threshold,
  724. },
  725. transport=__opts__['transport']
  726. )
  727. hc = conn.ex_create_healthcheck(
  728. name, host=host, path=path, port=port, interval=interval,
  729. timeout=timeout, unhealthy_threshold=unhealthy_threshold,
  730. healthy_threshold=healthy_threshold
  731. )
  732. salt.utils.cloud.fire_event(
  733. 'event',
  734. 'created health_check',
  735. 'salt/cloud/healthcheck/created',
  736. {
  737. 'name': name,
  738. 'host': host,
  739. 'path': path,
  740. 'port': port,
  741. 'interval': interval,
  742. 'timeout': timeout,
  743. 'unhealthy_threshold': unhealthy_threshold,
  744. 'healthy_threshold': healthy_threshold,
  745. },
  746. transport=__opts__['transport']
  747. )
  748. return _expand_item(hc)
  749. def delete_hc(kwargs=None, call=None):
  750. '''
  751. Permanently delete a health check.
  752. CLI Example:
  753. .. code-block:: bash
  754. salt-cloud -f delete_hc gce name=hc
  755. '''
  756. if call != 'function':
  757. raise SaltCloudSystemExit(
  758. 'The delete_hc function must be called with -f or --function.'
  759. )
  760. if not kwargs or 'name' not in kwargs:
  761. log.error(
  762. 'A name must be specified when deleting a health check.'
  763. )
  764. return False
  765. name = kwargs['name']
  766. conn = get_conn()
  767. salt.utils.cloud.fire_event(
  768. 'event',
  769. 'delete health_check',
  770. 'salt/cloud/healthcheck/deleting',
  771. {
  772. 'name': name,
  773. },
  774. transport=__opts__['transport']
  775. )
  776. try:
  777. result = conn.ex_destroy_healthcheck(
  778. conn.ex_get_healthcheck(name)
  779. )
  780. except ResourceNotFoundError as exc:
  781. log.error(
  782. 'Health check {0} could not be found.\n'
  783. 'The following exception was thrown by libcloud:\n{1}'.format(
  784. name, exc),
  785. exc_info_on_loglevel=logging.DEBUG
  786. )
  787. return False
  788. salt.utils.cloud.fire_event(
  789. 'event',
  790. 'deleted health_check',
  791. 'salt/cloud/healthcheck/deleted',
  792. {
  793. 'name': name,
  794. },
  795. transport=__opts__['transport']
  796. )
  797. return result
  798. def show_hc(kwargs=None, call=None):
  799. '''
  800. Show the details of an existing health check.
  801. CLI Example:
  802. .. code-block:: bash
  803. salt-cloud -f show_hc gce name=hc
  804. '''
  805. if call != 'function':
  806. raise SaltCloudSystemExit(
  807. 'The show_hc function must be called with -f or --function.'
  808. )
  809. if not kwargs or 'name' not in kwargs:
  810. log.error(
  811. 'Must specify name of health check.'
  812. )
  813. return False
  814. conn = get_conn()
  815. return _expand_item(conn.ex_get_healthcheck(kwargs['name']))
  816. def create_address(kwargs=None, call=None):
  817. '''
  818. Create a static address in a region.
  819. CLI Example:
  820. .. code-block:: bash
  821. salt-cloud -f create_address gce name=my-ip region=us-central1 address=IP
  822. '''
  823. if call != 'function':
  824. raise SaltCloudSystemExit(
  825. 'The create_address function must be called with -f or --function.'
  826. )
  827. if not kwargs or 'name' not in kwargs:
  828. log.error(
  829. 'A name must be specified when creating an address.'
  830. )
  831. return False
  832. if 'region' not in kwargs:
  833. log.error(
  834. 'A region must be specified for the address.'
  835. )
  836. return False
  837. name = kwargs['name']
  838. ex_region = kwargs['region']
  839. ex_address = kwargs.get("address", None)
  840. conn = get_conn()
  841. salt.utils.cloud.fire_event(
  842. 'event',
  843. 'create address',
  844. 'salt/cloud/address/creating',
  845. kwargs,
  846. transport=__opts__['transport']
  847. )
  848. addy = conn.ex_create_address(name, ex_region, ex_address)
  849. salt.utils.cloud.fire_event(
  850. 'event',
  851. 'created address',
  852. 'salt/cloud/address/created',
  853. kwargs,
  854. transport=__opts__['transport']
  855. )
  856. log.info('Created GCE Address '+name)
  857. return _expand_address(addy)
  858. def delete_address(kwargs=None, call=None):
  859. '''
  860. Permanently delete a static address.
  861. CLI Example:
  862. .. code-block:: bash
  863. salt-cloud -f delete_address gce name=my-ip
  864. '''
  865. if call != 'function':
  866. raise SaltCloudSystemExit(
  867. 'The delete_address function must be called with -f or --function.'
  868. )
  869. if not kwargs or 'name' not in kwargs:
  870. log.error(
  871. 'A name must be specified when deleting an address.'
  872. )
  873. return False
  874. if not kwargs or 'region' not in kwargs:
  875. log.error(
  876. 'A region must be specified when deleting an address.'
  877. )
  878. return False
  879. name = kwargs['name']
  880. ex_region = kwargs['region']
  881. conn = get_conn()
  882. salt.utils.cloud.fire_event(
  883. 'event',
  884. 'delete address',
  885. 'salt/cloud/address/deleting',
  886. {
  887. 'name': name,
  888. },
  889. transport=__opts__['transport']
  890. )
  891. try:
  892. result = conn.ex_destroy_address(
  893. conn.ex_get_address(name, ex_region)
  894. )
  895. except ResourceNotFoundError as exc:
  896. log.error(
  897. 'Address {0} could not be found (region {1})\n'
  898. 'The following exception was thrown by libcloud:\n{2}'.format(
  899. name, ex_region, exc),
  900. exc_info_on_loglevel=logging.DEBUG
  901. )
  902. return False
  903. salt.utils.cloud.fire_event(
  904. 'event',
  905. 'deleted address',
  906. 'salt/cloud/address/deleted',
  907. {
  908. 'name': name,
  909. },
  910. transport=__opts__['transport']
  911. )
  912. log.info('Deleted GCE Address ' + name)
  913. return result
  914. def show_address(kwargs=None, call=None):
  915. '''
  916. Show the details of an existing static address.
  917. CLI Example:
  918. .. code-block:: bash
  919. salt-cloud -f show_address gce name=mysnapshot region=us-central1
  920. '''
  921. if call != 'function':
  922. raise SaltCloudSystemExit(
  923. 'The show_snapshot function must be called with -f or --function.'
  924. )
  925. if not kwargs or 'name' not in kwargs:
  926. log.error(
  927. 'Must specify name.'
  928. )
  929. return False
  930. if not kwargs or 'region' not in kwargs:
  931. log.error(
  932. 'Must specify region.'
  933. )
  934. return False
  935. conn = get_conn()
  936. return _expand_address(conn.ex_get_address(kwargs['name'], kwargs['region']))
  937. def create_lb(kwargs=None, call=None):
  938. '''
  939. Create a load-balancer configuration.
  940. CLI Example:
  941. .. code-block:: bash
  942. salt-cloud -f create_lb gce name=lb region=us-central1 ports=80
  943. '''
  944. if call != 'function':
  945. raise SaltCloudSystemExit(
  946. 'The create_lb function must be called with -f or --function.'
  947. )
  948. if not kwargs or 'name' not in kwargs:
  949. log.error(
  950. 'A name must be specified when creating a health check.'
  951. )
  952. return False
  953. if 'ports' not in kwargs:
  954. log.error(
  955. 'A port or port-range must be specified for the load-balancer.'
  956. )
  957. return False
  958. if 'region' not in kwargs:
  959. log.error(
  960. 'A region must be specified for the load-balancer.'
  961. )
  962. return False
  963. if 'members' not in kwargs:
  964. log.error(
  965. 'A comma-separated list of members must be specified.'
  966. )
  967. return False
  968. name = kwargs['name']
  969. ports = kwargs['ports']
  970. ex_region = kwargs['region']
  971. members = kwargs.get('members').split(',')
  972. protocol = kwargs.get('protocol', 'tcp')
  973. algorithm = kwargs.get('algorithm', None)
  974. ex_healthchecks = kwargs.get('healthchecks', None)
  975. # pylint: disable=W0511
  976. conn = get_conn()
  977. lb_conn = get_lb_conn(conn)
  978. ex_address = kwargs.get('address', None)
  979. if ex_address is not None:
  980. ex_address = __create_orget_address(conn, ex_address, ex_region)
  981. if ex_healthchecks:
  982. ex_healthchecks = ex_healthchecks.split(',')
  983. salt.utils.cloud.fire_event(
  984. 'event',
  985. 'create load_balancer',
  986. 'salt/cloud/loadbalancer/creating',
  987. kwargs,
  988. transport=__opts__['transport']
  989. )
  990. lb = lb_conn.create_balancer(
  991. name, ports, protocol, algorithm, members,
  992. ex_region=ex_region, ex_healthchecks=ex_healthchecks,
  993. ex_address=ex_address
  994. )
  995. salt.utils.cloud.fire_event(
  996. 'event',
  997. 'created load_balancer',
  998. 'salt/cloud/loadbalancer/created',
  999. kwargs,
  1000. transport=__opts__['transport']
  1001. )
  1002. return _expand_balancer(lb)
  1003. def delete_lb(kwargs=None, call=None):
  1004. '''
  1005. Permanently delete a load-balancer.
  1006. CLI Example:
  1007. .. code-block:: bash
  1008. salt-cloud -f delete_lb gce name=lb
  1009. '''
  1010. if call != 'function':
  1011. raise SaltCloudSystemExit(
  1012. 'The delete_hc function must be called with -f or --function.'
  1013. )
  1014. if not kwargs or 'name' not in kwargs:
  1015. log.error(
  1016. 'A name must be specified when deleting a health check.'
  1017. )
  1018. return False
  1019. name = kwargs['name']
  1020. lb_conn = get_lb_conn(get_conn())
  1021. salt.utils.cloud.fire_event(
  1022. 'event',
  1023. 'delete load_balancer',
  1024. 'salt/cloud/loadbalancer/deleting',
  1025. {
  1026. 'name': name,
  1027. },
  1028. transport=__opts__['transport']
  1029. )
  1030. try:
  1031. result = lb_conn.destroy_balancer(
  1032. lb_conn.get_balancer(name)
  1033. )
  1034. except ResourceNotFoundError as exc:
  1035. log.error(
  1036. 'Load balancer {0} could not be found.\n'
  1037. 'The following exception was thrown by libcloud:\n{1}'.format(
  1038. name, exc),
  1039. exc_info_on_loglevel=logging.DEBUG
  1040. )
  1041. return False
  1042. salt.utils.cloud.fire_event(
  1043. 'event',
  1044. 'deleted load_balancer',
  1045. 'salt/cloud/loadbalancer/deleted',
  1046. {
  1047. 'name': name,
  1048. },
  1049. transport=__opts__['transport']
  1050. )
  1051. return result
  1052. def show_lb(kwargs=None, call=None):
  1053. '''
  1054. Show the details of an existing load-balancer.
  1055. CLI Example:
  1056. .. code-block:: bash
  1057. salt-cloud -f show_lb gce name=lb
  1058. '''
  1059. if call != 'function':
  1060. raise SaltCloudSystemExit(
  1061. 'The show_lb function must be called with -f or --function.'
  1062. )
  1063. if not kwargs or 'name' not in kwargs:
  1064. log.error(
  1065. 'Must specify name of load-balancer.'
  1066. )
  1067. return False
  1068. lb_conn = get_lb_conn(get_conn())
  1069. return _expand_balancer(lb_conn.get_balancer(kwargs['name']))
  1070. def attach_lb(kwargs=None, call=None):
  1071. '''
  1072. Add an existing node/member to an existing load-balancer configuration.
  1073. CLI Example:
  1074. .. code-block:: bash
  1075. salt-cloud -f attach_lb gce name=lb member=myinstance
  1076. '''
  1077. if call != 'function':
  1078. raise SaltCloudSystemExit(
  1079. 'The attach_lb function must be called with -f or --function.'
  1080. )
  1081. if not kwargs or 'name' not in kwargs:
  1082. log.error(
  1083. 'A load-balancer name must be specified.'
  1084. )
  1085. return False
  1086. if 'member' not in kwargs:
  1087. log.error(
  1088. 'A node name name must be specified.'
  1089. )
  1090. return False
  1091. conn = get_conn()
  1092. node = conn.ex_get_node(kwargs['member'])
  1093. lb_conn = get_lb_conn(conn)
  1094. lb = lb_conn.get_balancer(kwargs['name'])
  1095. salt.utils.cloud.fire_event(
  1096. 'event',
  1097. 'attach load_balancer',
  1098. 'salt/cloud/loadbalancer/attaching',
  1099. kwargs,
  1100. transport=__opts__['transport']
  1101. )
  1102. result = lb_conn.balancer_attach_compute_node(lb, node)
  1103. salt.utils.cloud.fire_event(
  1104. 'event',
  1105. 'attached load_balancer',
  1106. 'salt/cloud/loadbalancer/attached',
  1107. kwargs,
  1108. transport=__opts__['transport']
  1109. )
  1110. return _expand_item(result)
  1111. def detach_lb(kwargs=None, call=None):
  1112. '''
  1113. Remove an existing node/member from an existing load-balancer configuration.
  1114. CLI Example:
  1115. .. code-block:: bash
  1116. salt-cloud -f detach_lb gce name=lb member=myinstance
  1117. '''
  1118. if call != 'function':
  1119. raise SaltCloudSystemExit(
  1120. 'The detach_lb function must be called with -f or --function.'
  1121. )
  1122. if not kwargs or 'name' not in kwargs:
  1123. log.error(
  1124. 'A load-balancer name must be specified.'
  1125. )
  1126. return False
  1127. if 'member' not in kwargs:
  1128. log.error(
  1129. 'A node name name must be specified.'
  1130. )
  1131. return False
  1132. conn = get_conn()
  1133. lb_conn = get_lb_conn(conn)
  1134. lb = lb_conn.get_balancer(kwargs['name'])
  1135. member_list = lb_conn.balancer_list_members(lb)
  1136. remove_member = None
  1137. for member in member_list:
  1138. if member.id == kwargs['member']:
  1139. remove_member = member
  1140. break
  1141. if not remove_member:
  1142. log.error(
  1143. 'The specified member {0} was not a member of LB {1}.'.format(
  1144. kwargs['member'], kwargs['name']
  1145. )
  1146. )
  1147. return False
  1148. salt.utils.cloud.fire_event(
  1149. 'event',
  1150. 'detach load_balancer',
  1151. 'salt/cloud/loadbalancer/detaching',
  1152. kwargs,
  1153. transport=__opts__['transport']
  1154. )
  1155. result = lb_conn.balancer_detach_member(lb, remove_member)
  1156. salt.utils.cloud.fire_event(
  1157. 'event',
  1158. 'detached load_balancer',
  1159. 'salt/cloud/loadbalancer/detached',
  1160. kwargs,
  1161. transport=__opts__['transport']
  1162. )
  1163. return result
  1164. def delete_snapshot(kwargs=None, call=None):
  1165. '''
  1166. Permanently delete a disk snapshot.
  1167. CLI Example:
  1168. .. code-block:: bash
  1169. salt-cloud -f delete_snapshot gce name=disk-snap-1
  1170. '''
  1171. if call != 'function':
  1172. raise SaltCloudSystemExit(
  1173. 'The delete_snapshot function must be called with -f or --function.'
  1174. )
  1175. if not kwargs or 'name' not in kwargs:
  1176. log.error(
  1177. 'A name must be specified when deleting a snapshot.'
  1178. )
  1179. return False
  1180. name = kwargs['name']
  1181. conn = get_conn()
  1182. salt.utils.cloud.fire_event(
  1183. 'event',
  1184. 'delete snapshot',
  1185. 'salt/cloud/snapshot/deleting',
  1186. {
  1187. 'name': name,
  1188. },
  1189. transport=__opts__['transport']
  1190. )
  1191. try:
  1192. result = conn.destroy_volume_snapshot(
  1193. conn.ex_get_snapshot(name)
  1194. )
  1195. except ResourceNotFoundError as exc:
  1196. log.error(
  1197. 'Snapshot {0} could not be found.\n'
  1198. 'The following exception was thrown by libcloud:\n{1}'.format(
  1199. name, exc),
  1200. exc_info_on_loglevel=logging.DEBUG
  1201. )
  1202. return False
  1203. salt.utils.cloud.fire_event(
  1204. 'event',
  1205. 'deleted snapshot',
  1206. 'salt/cloud/snapshot/deleted',
  1207. {
  1208. 'name': name,
  1209. },
  1210. transport=__opts__['transport']
  1211. )
  1212. return result
  1213. def delete_disk(kwargs=None, call=None):
  1214. '''
  1215. Permanently delete a persistent disk.
  1216. CLI Example:
  1217. .. code-block:: bash
  1218. salt-cloud -f delete_disk gce disk_name=pd
  1219. '''
  1220. if call != 'function':
  1221. raise SaltCloudSystemExit(
  1222. 'The delete_disk function must be called with -f or --function.'
  1223. )
  1224. if not kwargs or 'disk_name' not in kwargs:
  1225. log.error(
  1226. 'A disk_name must be specified when deleting a disk.'
  1227. )
  1228. return False
  1229. conn = get_conn()
  1230. disk = conn.ex_get_volume(kwargs.get('disk_name'))
  1231. salt.utils.cloud.fire_event(
  1232. 'event',
  1233. 'delete disk',
  1234. 'salt/cloud/disk/deleting',
  1235. {
  1236. 'name': disk.name,
  1237. 'location': disk.extra['zone'].name,
  1238. 'size': disk.size,
  1239. },
  1240. transport=__opts__['transport']
  1241. )
  1242. try:
  1243. result = conn.destroy_volume(disk)
  1244. except ResourceInUseError as exc:
  1245. log.error(
  1246. 'Disk {0} is in use and must be detached before deleting.\n'
  1247. 'The following exception was thrown by libcloud:\n{1}'.format(
  1248. disk.name, exc),
  1249. exc_info_on_loglevel=logging.DEBUG
  1250. )
  1251. return False
  1252. salt.utils.cloud.fire_event(
  1253. 'event',
  1254. 'deleted disk',
  1255. 'salt/cloud/disk/deleted',
  1256. {
  1257. 'name': disk.name,
  1258. 'location': disk.extra['zone'].name,
  1259. 'size': disk.size,
  1260. },
  1261. transport=__opts__['transport']
  1262. )
  1263. return result
  1264. def create_disk(kwargs=None, call=None):
  1265. '''
  1266. Create a new persistent disk. Must specify `disk_name` and `location`.
  1267. Can also specify an `image` or `snapshot` but if neither of those are
  1268. specified, a `size` (in GB) is required.
  1269. CLI Example:
  1270. .. code-block:: bash
  1271. salt-cloud -f create_disk gce disk_name=pd size=300 location=us-central1-b
  1272. '''
  1273. if call != 'function':
  1274. raise SaltCloudSystemExit(
  1275. 'The create_disk function must be called with -f or --function.'
  1276. )
  1277. if kwargs is None:
  1278. kwargs = {}
  1279. name = kwargs.get('disk_name', None)
  1280. image = kwargs.get('image', None)
  1281. location = kwargs.get('location', None)
  1282. size = kwargs.get('size', None)
  1283. snapshot = kwargs.get('snapshot', None)
  1284. if location is None:
  1285. log.error(
  1286. 'A location (zone) must be specified when creating a disk.'
  1287. )
  1288. return False
  1289. if name is None:
  1290. log.error(
  1291. 'A disk_name must be specified when creating a disk.'
  1292. )
  1293. return False
  1294. if 'size' is None and 'image' is None and 'snapshot' is None:
  1295. log.error(
  1296. 'Must specify image, snapshot, or size.'
  1297. )
  1298. return False
  1299. conn = get_conn()
  1300. location = conn.ex_get_zone(kwargs['location'])
  1301. use_existing = True
  1302. salt.utils.cloud.fire_event(
  1303. 'event',
  1304. 'create disk',
  1305. 'salt/cloud/disk/creating',
  1306. {
  1307. 'name': name,
  1308. 'location': location.name,
  1309. 'image': image,
  1310. 'snapshot': snapshot,
  1311. },
  1312. transport=__opts__['transport']
  1313. )
  1314. disk = conn.create_volume(
  1315. size, name, location, snapshot, image, use_existing
  1316. )
  1317. salt.utils.cloud.fire_event(
  1318. 'event',
  1319. 'created disk',
  1320. 'salt/cloud/disk/created',
  1321. {
  1322. 'name': name,
  1323. 'location': location.name,
  1324. 'image': image,
  1325. 'snapshot': snapshot,
  1326. },
  1327. transport=__opts__['transport']
  1328. )
  1329. return _expand_disk(disk)
  1330. def create_snapshot(kwargs=None, call=None):
  1331. '''
  1332. Create a new disk snapshot. Must specify `name` and `disk_name`.
  1333. CLI Example:
  1334. .. code-block:: bash
  1335. salt-cloud -f create_snapshot gce name=snap1 disk_name=pd
  1336. '''
  1337. if call != 'function':
  1338. raise SaltCloudSystemExit(
  1339. 'The create_snapshot function must be called with -f or --function.'
  1340. )
  1341. if not kwargs or 'name' not in kwargs:
  1342. log.error(
  1343. 'A name must be specified when creating a snapshot.'
  1344. )
  1345. return False
  1346. if 'disk_name' not in kwargs:
  1347. log.error(
  1348. 'A disk_name must be specified when creating a snapshot.'
  1349. )
  1350. return False
  1351. conn = get_conn()
  1352. name = kwargs.get('name')
  1353. disk_name = kwargs.get('disk_name')
  1354. try:
  1355. disk = conn.ex_get_volume(disk_name)
  1356. except ResourceNotFoundError as exc:
  1357. log.error(
  1358. 'Disk {0} could not be found.\n'
  1359. 'The following exception was thrown by libcloud:\n{1}'.format(
  1360. disk_name, exc),
  1361. exc_info_on_loglevel=logging.DEBUG
  1362. )
  1363. return False
  1364. salt.utils.cloud.fire_event(
  1365. 'event',
  1366. 'create snapshot',
  1367. 'salt/cloud/snapshot/creating',
  1368. {
  1369. 'name': name,
  1370. 'disk_name': disk_name,
  1371. },
  1372. transport=__opts__['transport']
  1373. )
  1374. snapshot = conn.create_volume_snapshot(disk, name)
  1375. salt.utils.cloud.fire_event(
  1376. 'event',
  1377. 'created snapshot',
  1378. 'salt/cloud/snapshot/created',
  1379. {
  1380. 'name': name,
  1381. 'disk_name': disk_name,
  1382. },
  1383. transport=__opts__['transport']
  1384. )
  1385. return _expand_item(snapshot)
  1386. def show_disk(name=None, kwargs=None, call=None): # pylint: disable=W0613
  1387. '''
  1388. Show the details of an existing disk.
  1389. CLI Example:
  1390. .. code-block:: bash
  1391. salt-cloud -a show_disk myinstance disk_name=mydisk
  1392. salt-cloud -f show_disk gce disk_name=mydisk
  1393. '''
  1394. if not kwargs or 'disk_name' not in kwargs:
  1395. log.error(
  1396. 'Must specify disk_name.'
  1397. )
  1398. return False
  1399. conn = get_conn()
  1400. return _expand_disk(conn.ex_get_volume(kwargs['disk_name']))
  1401. def show_snapshot(kwargs=None, call=None):
  1402. '''
  1403. Show the details of an existing snapshot.
  1404. CLI Example:
  1405. .. code-block:: bash
  1406. salt-cloud -f show_snapshot gce name=mysnapshot
  1407. '''
  1408. if call != 'function':
  1409. raise SaltCloudSystemExit(
  1410. 'The show_snapshot function must be called with -f or --function.'
  1411. )
  1412. if not kwargs or 'name' not in kwargs:
  1413. log.error(
  1414. 'Must specify name.'
  1415. )
  1416. return False
  1417. conn = get_conn()
  1418. return _expand_item(conn.ex_get_snapshot(kwargs['name']))
  1419. def detach_disk(name=None, kwargs=None, call=None):
  1420. '''
  1421. Detach a disk from an instance.
  1422. CLI Example:
  1423. .. code-block:: bash
  1424. salt-cloud -a detach_disk myinstance disk_name=mydisk
  1425. '''
  1426. if call != 'action':
  1427. raise SaltCloudSystemExit(
  1428. 'The detach_Disk action must be called with -a or --action.'
  1429. )
  1430. if not name:
  1431. log.error(
  1432. 'Must specify an instance name.'
  1433. )
  1434. return False
  1435. if not kwargs or 'disk_name' not in kwargs:
  1436. log.error(
  1437. 'Must specify a disk_name to detach.'
  1438. )
  1439. return False
  1440. node_name = name
  1441. disk_name = kwargs['disk_name']
  1442. conn = get_conn()
  1443. node = conn.ex_get_node(node_name)
  1444. disk = conn.ex_get_volume(disk_name)
  1445. salt.utils.cloud.fire_event(
  1446. 'event',
  1447. 'detach disk',
  1448. 'salt/cloud/disk/detaching',
  1449. {
  1450. 'name': node_name,
  1451. 'disk_name': disk_name,
  1452. },
  1453. transport=__opts__['transport']
  1454. )
  1455. result = conn.detach_volume(disk, node)
  1456. salt.utils.cloud.fire_event(
  1457. 'event',
  1458. 'detached disk',
  1459. 'salt/cloud/disk/detached',
  1460. {
  1461. 'name': node_name,
  1462. 'disk_name': disk_name,
  1463. },
  1464. transport=__opts__['transport']
  1465. )
  1466. return result
  1467. def attach_disk(name=None, kwargs=None, call=None):
  1468. '''
  1469. Attach an existing disk to an existing instance.
  1470. CLI Example:
  1471. .. code-block:: bash
  1472. salt-cloud -a attach_disk myinstance disk_name=mydisk mode=READ_WRITE
  1473. '''
  1474. if call != 'action':
  1475. raise SaltCloudSystemExit(
  1476. 'The attach_disk action must be called with -a or --action.'
  1477. )
  1478. if not name:
  1479. log.error(
  1480. 'Must specify an instance name.'
  1481. )
  1482. return False
  1483. if not kwargs or 'disk_name' not in kwargs:
  1484. log.error(
  1485. 'Must specify a disk_name to attach.'
  1486. )
  1487. return False
  1488. node_name = name
  1489. disk_name = kwargs['disk_name']
  1490. mode = kwargs.get('mode', 'READ_WRITE').upper()
  1491. boot = kwargs.get('boot', False)
  1492. if boot and boot.lower() in ['true', 'yes', 'enabled']:
  1493. boot = True
  1494. else:
  1495. boot = False
  1496. if mode not in ['READ_WRITE', 'READ_ONLY']:
  1497. log.error(
  1498. 'Mode must be either READ_ONLY or (default) READ_WRITE.'
  1499. )
  1500. return False
  1501. conn = get_conn()
  1502. node = conn.ex_get_node(node_name)
  1503. disk = conn.ex_get_volume(disk_name)
  1504. salt.utils.cloud.fire_event(
  1505. 'event',
  1506. 'attach disk',
  1507. 'salt/cloud/disk/attaching',
  1508. {
  1509. 'name': node_name,
  1510. 'disk_name': disk_name,
  1511. 'mode': mode,
  1512. 'boot': boot,
  1513. },
  1514. transport=__opts__['transport']
  1515. )
  1516. result = conn.attach_volume(node, disk, ex_mode=mode, ex_boot=boot)
  1517. salt.utils.cloud.fire_event(
  1518. 'event',
  1519. 'attached disk',
  1520. 'salt/cloud/disk/attached',
  1521. {
  1522. 'name': node_name,
  1523. 'disk_name': disk_name,
  1524. 'mode': mode,
  1525. 'boot': boot,
  1526. },
  1527. transport=__opts__['transport']
  1528. )
  1529. return result
  1530. def reboot(vm_name, call=None):
  1531. '''
  1532. Call GCE 'reset' on the instance.
  1533. CLI Example:
  1534. .. code-block:: bash
  1535. salt-cloud -a reboot myinstance
  1536. '''
  1537. if call != 'action':
  1538. raise SaltCloudSystemExit(
  1539. 'The reboot action must be called with -a or --action.'
  1540. )
  1541. conn = get_conn()
  1542. return conn.reboot_node(
  1543. conn.ex_get_node(vm_name)
  1544. )
  1545. def destroy(vm_name, call=None):
  1546. '''
  1547. Call 'destroy' on the instance. Can be called with "-a destroy" or -d
  1548. CLI Example:
  1549. .. code-block:: bash
  1550. salt-cloud -a destroy myinstance1 myinstance2 ...
  1551. salt-cloud -d myinstance1 myinstance2 ...
  1552. '''
  1553. if call and call != 'action':
  1554. raise SaltCloudSystemExit(
  1555. 'The destroy action must be called with -d or "-a destroy".'
  1556. )
  1557. conn = get_conn()
  1558. try:
  1559. node = conn.ex_g