Guest User

Untitled

a guest
Feb 27th, 2018
54
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 17.01 KB | None | 0 0
  1. #!/usr/bin/python
  2. # -*- coding: utf-8 -*-
  3. # Copyright, (c) 2015, Joseph Callen <jcallen () csc.com>
  4. # Copyright, (c) 2018, Ansible Project
  5. # Copyright, (c) 2018, Abhijeet Kasurde <akasurde@redhat.com>
  6. # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
  7.  
  8. from __future__ import absolute_import, division, print_function
  9. __metaclass__ = type
  10.  
  11.  
  12. ANSIBLE_METADATA = {
  13. 'metadata_version': '1.1',
  14. 'status': ['preview'],
  15. 'supported_by': 'community'
  16. }
  17.  
  18.  
  19. DOCUMENTATION = '''
  20. ---
  21. module: vmware_vswitch
  22. short_description: Manage a VMware Standard Switch to an ESXi host.
  23. description:
  24. - This module can be used to add, remove and update a VMware Standard Switch to an ESXi host.
  25. version_added: 2.0
  26. author:
  27. - Joseph Callen (@jcpowermac)
  28. - Russell Teague (@mtnbikenc)
  29. - Abhijeet Kasurde (@akasurde) <akasurde@redhat.com>
  30. notes:
  31. - Tested on vSphere 5.5 and 6.5
  32. requirements:
  33. - python >= 2.6
  34. - PyVmomi
  35. options:
  36. switch:
  37. description:
  38. - vSwitch name to add.
  39. - Alias C(switch) is added in version 2.4.
  40. required: yes
  41. aliases: [ switch_name ]
  42. nics:
  43. description:
  44. - A list of vmnic names or vmnic name to attach to vSwitch.
  45. - Alias C(nics) is added in version 2.4.
  46. aliases: [ nic_name ]
  47. default: []
  48. number_of_ports:
  49. description:
  50. - Number of port to configure on vSwitch.
  51. default: 128
  52. mtu:
  53. description:
  54. - MTU to configure on vSwitch.
  55. default: 1500
  56. state:
  57. description:
  58. - Add or remove the switch.
  59. default: present
  60. choices: [ absent, present ]
  61. esxi_hostname:
  62. description:
  63. - Manage the vSwitch using this ESXi host system.
  64. version_added: "2.5"
  65. aliases: [ 'host' ]
  66. extends_documentation_fragment:
  67. - vmware.documentation
  68. '''
  69.  
  70. EXAMPLES = '''
  71. - name: Add a VMware vSwitch
  72. action:
  73. module: vmware_vswitch
  74. hostname: esxi_hostname
  75. username: esxi_username
  76. password: esxi_password
  77. switch: vswitch_name
  78. nics: vmnic_name
  79. mtu: 9000
  80. delegate_to: localhost
  81.  
  82. - name: Add a VMWare vSwitch without any physical NIC attached
  83. vmware_vswitch:
  84. hostname: 192.168.10.1
  85. username: admin
  86. password: password123
  87. switch: vswitch_0001
  88. mtu: 9000
  89. delegate_to: localhost
  90.  
  91. - name: Add a VMWare vSwitch with multiple NICs
  92. vmware_vswitch:
  93. hostname: esxi_hostname
  94. username: esxi_username
  95. password: esxi_password
  96. switch: vmware_vswitch_0004
  97. nics:
  98. - vmnic1
  99. - vmnic2
  100. mtu: 9000
  101. delegate_to: localhost
  102.  
  103. - name: Add a VMware vSwitch to a specific host system
  104. vmware_vswitch:
  105. hostname: 192.168.10.1
  106. username: esxi_username
  107. password: esxi_password
  108. esxi_hostname: DC0_H0
  109. switch_name: vswitch_001
  110. nic_name: vmnic0
  111. mtu: 9000
  112. delegate_to: localhost
  113. '''
  114.  
  115. RETURN = """
  116. result:
  117. description: information about performed operation
  118. returned: always
  119. type: string
  120. sample: "vSwitch 'vSwitch_1002' is created successfully"
  121. """
  122.  
  123. try:
  124. from pyVmomi import vim, vmodl
  125. except ImportError:
  126. pass
  127.  
  128. from ansible.module_utils.basic import AnsibleModule
  129. from ansible.module_utils.vmware import PyVmomi, vmware_argument_spec
  130. from ansible.module_utils._text import to_native
  131.  
  132.  
  133. class VMwareHostVirtualSwitch(PyVmomi):
  134. def __init__(self, module):
  135. super(VMwareHostVirtualSwitch, self).__init__(module)
  136. self.host_system = None
  137. self.vss = None
  138. self.switch = module.params['switch']
  139. self.number_of_ports = module.params['number_of_ports']
  140. self.nics = module.params['nics']
  141. self.mtu = module.params['mtu']
  142. self.state = module.params['state']
  143. esxi_hostname = module.params['esxi_hostname']
  144.  
  145. self.host_system = self.get_all_host_objs(esxi_host_name=esxi_hostname)[0]
  146.  
  147. if self.params.get('state') == 'present':
  148. # Gather information about all vSwitches and Physical NICs
  149. network_manager = self.host_system.configManager.networkSystem
  150. available_pnic = [pnic.device for pnic in network_manager.networkInfo.pnic]
  151. self.available_vswitches = dict()
  152. for available_vswitch in network_manager.networkInfo.vswitch:
  153. used_pnic = []
  154. for pnic in available_vswitch.pnic:
  155. # vSwitch contains all PNICs as string in format of 'key-vim.host.PhysicalNic-vmnic0'
  156. m_pnic = pnic.split("-", 3)[-1]
  157. used_pnic.append(m_pnic)
  158. self.available_vswitches[available_vswitch.name] = dict(pnic=used_pnic,
  159. mtu=available_vswitch.mtu,
  160. num_ports=available_vswitch.numPorts,
  161. )
  162. for desired_pnic in self.nics:
  163. if desired_pnic not in available_pnic:
  164. # Check if pnic does not exists
  165. self.module.fail_json(msg="Specified Physical NIC '%s' does not"
  166. " exists on given ESXi '%s'." % (desired_pnic,
  167. self.host_system.name))
  168. for vswitch in self.available_vswitches:
  169. if desired_pnic in self.available_vswitches[vswitch]['pnic'] and vswitch != self.switch:
  170. # Check if pnic is already part of some other vSwitch
  171. self.module.fail_json(msg="Specified Physical NIC '%s' is already used"
  172. " by vSwitch '%s'." % (desired_pnic, vswitch))
  173.  
  174. def process_state(self):
  175. """
  176. Function to manage internal state of vSwitch
  177. """
  178. vswitch_states = {
  179. 'absent': {
  180. 'present': self.state_destroy_vswitch,
  181. 'absent': self.state_exit_unchanged,
  182. },
  183. 'present': {
  184. 'update': self.state_update_vswitch,
  185. 'present': self.state_exit_unchanged,
  186. 'absent': self.state_create_vswitch,
  187. }
  188. }
  189.  
  190. try:
  191. vswitch_states[self.state][self.check_vswitch_configuration()]()
  192. except vmodl.RuntimeFault as runtime_fault:
  193. self.module.fail_json(msg=to_native(runtime_fault.msg))
  194. except vmodl.MethodFault as method_fault:
  195. self.module.fail_json(msg=to_native(method_fault.msg))
  196. except Exception as e:
  197. self.module.fail_json(msg=to_native(e))
  198.  
  199. def state_create_vswitch(self):
  200. """
  201. Function to create a virtual switch
  202.  
  203. Source from
  204. https://github.com/rreubenur/pyvmomi-community-samples/blob/patch-1/samples/create_vswitch.py
  205.  
  206. """
  207.  
  208. results = dict(changed=False, result="")
  209. vss_spec = vim.host.VirtualSwitch.Specification()
  210. vss_spec.numPorts = self.number_of_ports
  211. vss_spec.mtu = self.mtu
  212. if self.nics:
  213. vss_spec.bridge = vim.host.VirtualSwitch.BondBridge(nicDevice=self.nics)
  214. try:
  215. network_mgr = self.host_system.configManager.networkSystem
  216. if network_mgr:
  217. network_mgr.AddVirtualSwitch(vswitchName=self.switch,
  218. spec=vss_spec)
  219. results['changed'] = True
  220. results['result'] = "vSwitch '%s' is created successfully" % self.switch
  221. else:
  222. self.module.fail_json(msg="Failed to find network manager for ESXi system")
  223. except vim.fault.AlreadyExists as already_exists:
  224. results['result'] = "vSwitch with name %s already exists: %s" % (self.switch,
  225. to_native(already_exists.msg))
  226. except vim.fault.ResourceInUse as resource_used:
  227. self.module.fail_json(msg="Failed to add vSwitch '%s' as physical network adapter"
  228. " being bridged is already in use: %s" % (self.switch,
  229. to_native(resource_used.msg)))
  230. except vim.fault.HostConfigFault as host_config_fault:
  231. self.module.fail_json(msg="Failed to add vSwitch '%s' due to host"
  232. " configuration fault : %s" % (self.switch,
  233. to_native(host_config_fault.msg)))
  234. except vmodl.fault.InvalidArgument as invalid_argument:
  235. self.module.fail_json(msg="Failed to add vSwitch '%s', this can be due to either of following :"
  236. " 1. vSwitch Name exceeds the maximum allowed length,"
  237. " 2. Number of ports specified falls out of valid range,"
  238. " 3. Network policy is invalid,"
  239. " 4. Beacon configuration is invalid : %s" % (self.switch,
  240. to_native(invalid_argument.msg)))
  241. except vmodl.fault.SystemError as system_error:
  242. self.module.fail_json(msg="Failed to add vSwitch '%s' due to : %s" % (self.switch,
  243. to_native(system_error.msg)))
  244. except Exception as generic_exc:
  245. self.module.fail_json(msg="Failed to add vSwitch '%s' due to"
  246. " generic exception : %s" % (self.switch,
  247. to_native(generic_exc)))
  248. self.module.exit_json(**results)
  249.  
  250. def state_exit_unchanged(self):
  251. """
  252. Function to declare exit without unchanged
  253. """
  254. self.module.exit_json(changed=False)
  255.  
  256. def state_destroy_vswitch(self):
  257. """
  258. Function to remove vSwitch from configuration
  259.  
  260. """
  261. results = dict(changed=False, result="")
  262.  
  263. try:
  264. self.host_system.configManager.networkSystem.RemoveVirtualSwitch(self.vss.name)
  265. results['changed'] = True
  266. results['result'] = "vSwitch '%s' removed successfully." % self.vss.name
  267. except vim.fault.NotFound as vswitch_not_found:
  268. results['result'] = "vSwitch '%s' not available. %s" % (self.switch,
  269. to_native(vswitch_not_found.msg))
  270. except vim.fault.ResourceInUse as vswitch_in_use:
  271. self.module.fail_json(msg="Failed to remove vSwitch '%s' as vSwitch"
  272. " is used by several virtual"
  273. " network adapters: %s" % (self.switch,
  274. to_native(vswitch_in_use.msg)))
  275. except vim.fault.HostConfigFault as host_config_fault:
  276. self.module.fail_json(msg="Failed to remove vSwitch '%s' due to host"
  277. " configuration fault : %s" % (self.switch,
  278. to_native(host_config_fault.msg)))
  279. except Exception as generic_exc:
  280. self.module.fail_json(msg="Failed to remove vSwitch '%s' due to generic"
  281. " exception : %s" % (self.switch,
  282. to_native(generic_exc)))
  283.  
  284. self.module.exit_json(**results)
  285.  
  286. def state_update_vswitch(self):
  287. """
  288. Function to update vSwitch
  289.  
  290. """
  291. results = dict(changed=False, result="")
  292. vswitch_pnic_info = self.available_vswitches[self.switch]
  293. remain_pnic = []
  294. for desired_pnic in self.nics:
  295. if desired_pnic not in vswitch_pnic_info['pnic']:
  296. remain_pnic.append(desired_pnic)
  297.  
  298. vss_spec = vim.host.VirtualSwitch.Specification()
  299. if remain_pnic:
  300. vss_spec.bridge = vim.host.VirtualSwitch.BondBridge(nicDevice=remain_pnic)
  301.  
  302. if vswitch_pnic_info['mtu'] != self.mtu:
  303. vss_spec.mtu = self.mtu
  304.  
  305. if vswitch_pnic_info['num_ports'] != self.number_of_ports:
  306. vss_spec.numPorts = self.number_of_ports
  307.  
  308. try:
  309. network_mgr = self.host_system.configManager.networkSystem
  310. if network_mgr:
  311. network_mgr.UpdateVirtualSwitch(vswitchName=self.switch,
  312. spec=vss_spec)
  313. results['changed'] = True
  314. results['result'] = "vSwitch '%s' is updated successfully" % self.switch
  315. else:
  316. self.module.fail_json(msg="Failed to find network manager for ESXi system.")
  317. except vim.fault.ResourceInUse as resource_used:
  318. self.module.fail_json(msg="Failed to update vSwitch '%s' as physical network adapter"
  319. " being bridged is already in use: %s" % (self.switch,
  320. to_native(resource_used.msg)))
  321. except vim.fault.NotFound as not_found:
  322. self.module.fail_json(msg="Failed to update vSwitch with name '%s'"
  323. " as it does not exists: %s" % (self.switch,
  324. to_native(not_found.msg)))
  325.  
  326. except vim.fault.HostConfigFault as host_config_fault:
  327. self.module.fail_json(msg="Failed to update vSwitch '%s' due to host"
  328. " configuration fault : %s" % (self.switch,
  329. to_native(host_config_fault.msg)))
  330. except vmodl.fault.InvalidArgument as invalid_argument:
  331. self.module.fail_json(msg="Failed to update vSwitch '%s', this can be due to either of following :"
  332. " 1. vSwitch Name exceeds the maximum allowed length,"
  333. " 2. Number of ports specified falls out of valid range,"
  334. " 3. Network policy is invalid,"
  335. " 4. Beacon configuration is invalid : %s" % (self.switch,
  336. to_native(invalid_argument.msg)))
  337. except vmodl.fault.SystemError as system_error:
  338. self.module.fail_json(msg="Failed to update vSwitch '%s' due to : %s" % (self.switch,
  339. to_native(system_error.msg)))
  340. except vmodl.fault.NotSupported as not_supported:
  341. self.module.fail_json(msg="Failed to update vSwitch '%s' as network adapter teaming policy"
  342. " is set but is not supported : %s" % (self.switch,
  343. to_native(not_supported.msg)))
  344. except Exception as generic_exc:
  345. self.module.fail_json(msg="Failed to add vSwitch '%s' due to"
  346. " generic exception : %s" % (self.switch,
  347. to_native(generic_exc)))
  348. self.module.exit_json(**results)
  349.  
  350. def check_vswitch_configuration(self):
  351. """
  352. Function to check if vSwitch exists
  353. Returns: 'present' if vSwitch exists or 'absent' if not
  354.  
  355. """
  356. self.vss = self.find_vswitch_by_name(self.host_system, self.switch)
  357. if self.vss is None:
  358. return 'absent'
  359. else:
  360. return 'present'
  361.  
  362. @staticmethod
  363. def find_vswitch_by_name(host, vswitch_name):
  364. """
  365. Function to find and return vSwitch managed object
  366. Args:
  367. host: Host system managed object
  368. vswitch_name: Name of vSwitch to find
  369.  
  370. Returns: vSwitch managed object if found, else None
  371.  
  372. """
  373. for vss in host.configManager.networkSystem.networkInfo.vswitch:
  374. if vss.name == vswitch_name:
  375. return vss
  376. return None
  377.  
  378.  
  379. def main():
  380. argument_spec = vmware_argument_spec()
  381. argument_spec.update(dict(
  382. switch=dict(type='str', required=True, aliases=['switch_name']),
  383. nics=dict(type='list', aliases=['nic_name'], default=[]),
  384. number_of_ports=dict(type='int', default=128),
  385. mtu=dict(type='int', default=1500),
  386. state=dict(type='str', default='present', choices=['absent', 'present'])),
  387. esxi_hostname=dict(type='str', aliases=['host']),
  388. )
  389.  
  390. module = AnsibleModule(argument_spec=argument_spec,
  391. supports_check_mode=False)
  392.  
  393. host_virtual_switch = VMwareHostVirtualSwitch(module)
  394. host_virtual_switch.process_state()
  395.  
  396.  
  397. if __name__ == '__main__':
  398. main()
Add Comment
Please, Sign In to add comment