- Infrastrukturní DevOps s HPE OneView (1) – Infrastructure as Code
- Infrastrukturní DevOps s HPE OneView (2) – API
- Infrastrukturní DevOps s HPE OneView (3) – Message bus
- Infrastrukturní DevOps s HPE OneView (4) – PowerShell
- Infrastrukturní DevOps s HPE OneView (5) – PowerShell skripty
- Infrastrukturní DevOps s HPE OneView (6) – Python
- Infrastrukturní DevOps s HPE OneView (7) – Python skripty
- Infrastrukturní DevOps s HPE OneView (8) – vaše vlastní aplikace s Grommet
- Infrastrukturní DevOps s HPE OneView (9) – Ansible a infrastruktura
- Infrastrukturní DevOps s HPE OneView (10) – Ansible a Blade networking
- Infrastrukturní DevOps s HPE OneView (11) – Ansible a síťový fabric
- Infrastrukturní DevOps s HPE OneView (12) – Ansible a servery
V dnešním díle už se dostává k vyšším abstrakcím. Od nízkoúrovňového zkoumání API jsme se dostali ke skriptům v PowerShell a Python a ukázali si příklad aplikace s využitím OneView API a Grommet uživatelského rozhraní. Dnes začneme Ansible – nemusíte vůbec umět programovat, abyste ho dokázali využít. Zajišťuje jednotný popis infrastruktury, opakovatelnost a je to open source projekt.
Příprava Ansible prostředí
Nejprve si připravme prostředí. Kompletní příklad včetně všech detailů najdete na mém GitHub účtu:
https://github.com/tkubica12/oneview-demo/tree/master/ansible
V tomto adresáři je skript install.sh, kterým můžete ve své Linux VM nainstalovat potřebné komponenty, tedy samotný Ansible, OneView Python knihovnu a OneView Ansible moduly.
Začněme výsledkem, detaily později
Popis infrastruktury
Výsledek našeho snažení s Ansible je vidět ve dvou souborech, které ukazují, jak lze vrstvit abstrakce. My se v pozdějších dílech seriálu podíváme na detaily, jak jsme si sami napsali Ansible role, aby to dělalo co chceme (a pokud chcete, můžete se kouknout i pod kapotu Ansible modulů, tedy nižších abstrakcích napsaných přímo HPE inženýry jako open source). To teď nechme stranou.
V hlavním adresáři najdete soubor config.yaml (YAML je strukturovaný zápis dat, který je velmi dobře lidsky i strojově čitelný) a v něm je popis infrastruktury. Co tedy chceme, aby bylo nastaveno. Podívejme se dovnitř.
vlans: - id: 101 name: Prod-101 - id: 102 name: Prod-102 - id: 103 name: Dev-103 logical_interconnect_group: FlexFabric connectivity_enclosures: - logical_interconnects: - module: Encl1, interconnect 1 ports: - X2 - X4 switchports: - GigabitEthernet 1/0/2 - GigabitEthernet 1/0/3 switch_link_aggregation_group: 1 - module: Encl1, interconnect 2 ports: - X2 - X4 switchports: - GigabitEthernet 1/0/4 - GigabitEthernet 1/0/5 switch_link_aggregation_group: 2 - logical_interconnects: - module: Encl2, interconnect 1 ports: - X2 - X4 switchports: - GigabitEthernet 1/0/6 - GigabitEthernet 1/0/7 switch_link_aggregation_group: 3 - module: Encl2, interconnect 2 ports: - X2 - X4 switchports: - GigabitEthernet 1/0/8 - GigabitEthernet 1/0/9 # switch_link_aggregation_group: 4 server_profiles: - name: DB_servers hardware_type: /rest/server-hardware-types/993EE3AD-44BB-42F5-86BB-DCCCB4BE5CE6 enclosure_group: ENCL-group ethernet_networks: - Prod-101 - Prod-102 - name: APP_servers hardware_type: /rest/server-hardware-types/993EE3AD-44BB-42F5-86BB-DCCCB4BE5CE6 enclosure_group: ENCL-group ethernet_networks: - Dev-103 servers: - name: My_DB_1 profile: DB_servers - name: My_DB_2 profile: DB_servers - name: My_APP_1 profile: APP_servers - name: My_APP_2 profile: APP_servers
Nejprve jsou tam definice sítí, jejich názvů a VLAN ID. Rád bych, aby tyto byly vytvořeny v blade systému a jeho interconnectech (to je řízeno z OneView), ale také v top-of-rack prvku, který Oneview neřídí. Dále definuji jaké interconnecty v bladu jsou, jaké porty mají vytvořit linkovou agregaci a do jakých portů síťového top-of-rack prvku jsou připojeny. Tady očekávám, že se na těchto portech objeví VLANy, které jsou aktuálně potřeba a to jak v interconnectu tak venkovním prvku a to včetně nastavení linkové agregace (pokud je požadována).
Pak definujeme šablony serverových profilů. To je něco, co ve OneView můžete naklikat v GUI, ale my tento proces chceme automatizovat a držet v tomto jednotném popisu infrastruktury. V mém případě jde o dvě šablony a nějaké sítě.
V posledním kroku je zapsáno jaké servery chci, aby mi Ansible zajistil. Konkrétně jsou to dva DB servery a dva aplikační servery.
Hlavní vykonávací soubor
Logika toho, jak se z výše uvedeného popisu infrastruktury dostat do finálního stavu je zachycena v souboru main.yaml. Podívejme se na něj, ale možná se vám bude zdát podezřele jednoduchý.
--- - name: Blade networking hosts: localhost gather_facts: no connection: local vars: oneview_config_file: "oneview-config.json" state: present vars_files: - config.yaml roles: - ov-networking - name: Top-of-rack networking hosts: switches gather_facts: no connection: local vars: state: present vars_files: - config.yaml roles: - tor-networking - name: Servers hosts: localhost gather_facts: no connection: local vars: oneview_config_file: "oneview-config.json" state: present vars_files: - config.yaml roles: - server-profiles
Vlastně jen říkáme tady je konfigurace infrastruktury, tady jsou přihlašovací údaje a nejdřív nastav síťové věci ve OneView, pak nastav síť v top-of-rack prvku a pak zajisti servery, tedy šablony profilů a přiřazené profily.
Takhle jednoduché to je z toho důvodu, že jsme použili role. Spousta logiky jak se toho dosáhne je v oné roli, kterou pak už jen vyvoláme. Role je něco, co jsem si napsal a takto používám. Nicméně pokud budeme mít stejný nápad, můžeme role sdílet a použijete je, aniž byste museli rozumět jejich detailům. Přesně takhle se v Ansible typicky využívají role například pro instalaci nějakého software do výsledného serveru. Na detaily jak to funguje se podíváme příště, ale všechno najdete v adresáři roles/tasks, pokud to chcete prozkoumat už teď.
Jak to vypadá, když to pracuje
Konfigurační soubor máme, prováděcí předpis také, můžeme to spustit. Ansible projde požadovaný stav a srovná ho se skutečným. Co nebude sedět (chybějící síť, nenastavený port, chybějící server apod.), opraví, tedy uvede infrastrukturu do stavu požadovaného. Podívejme jak to probíhá:
$ ansible-playbook -i hosts main.yaml PLAY [Blade networking] ******************************************************** TASK [ov-networking : Ensure that Networks exist] ****************************** ok: [localhost] => (item={u'id': 101, u'name': u'Prod-101'}) ok: [localhost] => (item={u'id': 102, u'name': u'Prod-102'}) ok: [localhost] => (item={u'id': 103, u'name': u'Dev-103'}) TASK [ov-networking : Store network URIs in list] ****************************** ok: [localhost] TASK [ov-networking : Ensure networks are present on Logical interconnect group] *** changed: [localhost] TASK [ov-networking : Ensure UplinkSets are configured] ************************ included: /home/hpe/oneview-demo/ansible/roles/ov-networking/tasks/uplinksets.yaml for localhost included: /home/hpe/oneview-demo/ansible/roles/ov-networking/tasks/uplinksets.yaml for localhost TASK [ov-networking : Create map with facts about interconnects] *************** [WARNING]: The loop variable 'item' is already in use. You should set the `loop_var` value in the `loop_control` option for the task to something else to avoid variable collisions and unexpected behavior. ok: [localhost] => (item={u'ports': [u'X2', u'X4'], u'switch_link_aggregation_group': 1, u'module': u'Encl1, interconnect 1', u'switchports': [u'GigabitEthernet 1/0/2', u'GigabitEthernet 1/0/3']}) ok: [localhost] => (item={u'ports': [u'X2', u'X4'], u'switch_link_aggregation_group': 2, u'module': u'Encl1, interconnect 2', u'switchports': [u'GigabitEthernet 1/0/4', u'GigabitEthernet 1/0/5']}) TASK [ov-networking : Build port configurations] ******************************* [WARNING]: The loop variable 'item' is already in use. You should set the `loop_var` value in the `loop_control` option for the task to something else to avoid variable collisions and unexpected behavior. ok: [localhost] => ... TASK [ov-networking : Map port configurations to JSON array] ******************* ok: [localhost] TASK [ov-networking : Get Logical Interconnect URI] **************************** ok: [localhost] TASK [ov-networking : Ensure that the Uplink Set with Networks is present] ***** changed: [localhost] TASK [ov-networking : Create map with facts about interconnects] *************** [WARNING]: The loop variable 'item' is already in use. You should set the `loop_var` value in the `loop_control` option for the task to something else to avoid variable collisions and unexpected behavior. ok: [localhost] => (item={u'ports': [u'X2', u'X4'], u'switch_link_aggregation_group': 3, u'module': u'Encl2, interconnect 1', u'switchports': [u'GigabitEthernet 1/0/6', u'GigabitEthernet 1/0/7']}) ok: [localhost] => (item={u'ports': [u'X2', u'X4'], u'module': u'Encl2, interconnect 2', u'switchports': [u'GigabitEthernet 1/0/8', u'GigabitEthernet 1/0/9']}) TASK [ov-networking : Build port configurations] ******************************* [WARNING]: The loop variable 'item' is already in use. You should set the `loop_var` value in the `loop_control` option for the task to something else to avoid variable collisions and unexpected behavior. ok: [localhost] => ... TASK [ov-networking : Map port configurations to JSON array] ******************* ok: [localhost] TASK [ov-networking : Get Logical Interconnect URI] **************************** ok: [localhost] TASK [ov-networking : Ensure that the Uplink Set with Networks is present] ***** changed: [localhost] PLAY [Top-of-rack networking] ************************************************** TASK [tor-networking : Ensure that VLANs exist] ******************************** ok: [192.168.56.10] => (item={u'id': 101, u'name': u'Prod-101'}) ok: [192.168.56.10] => (item={u'id': 102, u'name': u'Prod-102'}) ok: [192.168.56.10] => (item={u'id': 103, u'name': u'Dev-103'}) TASK [tor-networking : Create permited VLANs string] *************************** ok: [192.168.56.10] TASK [tor-networking : Ensure ports are configured] **************************** included: /home/hpe/oneview-demo/ansible/roles/tor-networking/tasks/ports.yaml for 192.168.56.10 included: /home/hpe/oneview-demo/ansible/roles/tor-networking/tasks/ports.yaml for 192.168.56.10 TASK [tor-networking : Ensure link aggregation is configured] ****************** [WARNING]: The loop variable 'item' is already in use. You should set the `loop_var` value in the `loop_control` option for the task to something else to avoid variable collisions and unexpected behavior. changed: [192.168.56.10] => (item={u'ports': [u'X2', u'X4'], u'switch_link_aggregation_group': 1, u'module': u'Encl1, interconnect 1', u'switchports': [u'GigabitEthernet 1/0/2', u'GigabitEthernet 1/0/3']}) changed: [192.168.56.10] => (item={u'ports': [u'X2', u'X4'], u'switch_link_aggregation_group': 2, u'module': u'Encl1, interconnect 2', u'switchports': [u'GigabitEthernet 1/0/4', u'GigabitEthernet 1/0/5']}) TASK [tor-networking : Ensure that VLANs are configured on standalone ports] *** [WARNING]: The loop variable 'item' is already in use. You should set the `loop_var` value in the `loop_control` option for the task to something else to avoid variable collisions and unexpected behavior. skipping: [192.168.56.10] => (item=({u'switch_link_aggregation_group': 1, u'module': u'Encl1, interconnect 1', u'ports': [u'X2', u'X4']}, u'GigabitEthernet 1/0/2')) skipping: [192.168.56.10] => (item=({u'switch_link_aggregation_group': 1, u'module': u'Encl1, interconnect 1', u'ports': [u'X2', u'X4']}, u'GigabitEthernet 1/0/3')) skipping: [192.168.56.10] => (item=({u'switch_link_aggregation_group': 2, u'module': u'Encl1, interconnect 2', u'ports': [u'X2', u'X4']}, u'GigabitEthernet 1/0/4')) skipping: [192.168.56.10] => (item=({u'switch_link_aggregation_group': 2, u'module': u'Encl1, interconnect 2', u'ports': [u'X2', u'X4']}, u'GigabitEthernet 1/0/5')) TASK [tor-networking : Ensure that VLANs are configured on link aggregattion interfaces] *** [WARNING]: The loop variable 'item' is already in use. You should set the `loop_var` value in the `loop_control` option for the task to something else to avoid variable collisions and unexpected behavior. changed: [192.168.56.10] => (item={u'ports': [u'X2', u'X4'], u'switch_link_aggregation_group': 1, u'module': u'Encl1, interconnect 1', u'switchports': [u'GigabitEthernet 1/0/2', u'GigabitEthernet 1/0/3']}) changed: [192.168.56.10] => (item={u'ports': [u'X2', u'X4'], u'switch_link_aggregation_group': 2, u'module': u'Encl1, interconnect 2', u'switchports': [u'GigabitEthernet 1/0/4', u'GigabitEthernet 1/0/5']}) TASK [tor-networking : Ensure link aggregation is configured] ****************** [WARNING]: The loop variable 'item' is already in use. You should set the `loop_var` value in the `loop_control` option for the task to something else to avoid variable collisions and unexpected behavior. changed: [192.168.56.10] => (item={u'ports': [u'X2', u'X4'], u'switch_link_aggregation_group': 3, u'module': u'Encl2, interconnect 1', u'switchports': [u'GigabitEthernet 1/0/6', u'GigabitEthernet 1/0/7']}) skipping: [192.168.56.10] => (item={u'ports': [u'X2', u'X4'], u'module': u'Encl2, interconnect 2', u'switchports': [u'GigabitEthernet 1/0/8', u'GigabitEthernet 1/0/9']}) TASK [tor-networking : Ensure that VLANs are configured on standalone ports] *** [WARNING]: The loop variable 'item' is already in use. You should set the `loop_var` value in the `loop_control` option for the task to something else to avoid variable collisions and unexpected behavior. skipping: [192.168.56.10] => (item=({u'switch_link_aggregation_group': 3, u'module': u'Encl2, interconnect 1', u'ports': [u'X2', u'X4']}, u'GigabitEthernet 1/0/6')) skipping: [192.168.56.10] => (item=({u'switch_link_aggregation_group': 3, u'module': u'Encl2, interconnect 1', u'ports': [u'X2', u'X4']}, u'GigabitEthernet 1/0/7')) changed: [192.168.56.10] => (item=({u'ports': [u'X2', u'X4'], u'module': u'Encl2, interconnect 2'}, u'GigabitEthernet 1/0/8')) changed: [192.168.56.10] => (item=({u'ports': [u'X2', u'X4'], u'module': u'Encl2, interconnect 2'}, u'GigabitEthernet 1/0/9')) TASK [tor-networking : Ensure that VLANs are configured on link aggregattion interfaces] *** [WARNING]: The loop variable 'item' is already in use. You should set the `loop_var` value in the `loop_control` option for the task to something else to avoid variable collisions and unexpected behavior. changed: [192.168.56.10] => (item={u'ports': [u'X2', u'X4'], u'switch_link_aggregation_group': 3, u'module': u'Encl2, interconnect 1', u'switchports': [u'GigabitEthernet 1/0/6', u'GigabitEthernet 1/0/7']}) skipping: [192.168.56.10] => (item={u'ports': [u'X2', u'X4'], u'module': u'Encl2, interconnect 2', u'switchports': [u'GigabitEthernet 1/0/8', u'GigabitEthernet 1/0/9']}) TASK [tor-networking : Save switch configuration] ****************************** changed: [192.168.56.10] PLAY [Servers] ***************************************************************** TASK [server-profiles : Ensure server profiles templates are presentt] ********* included: /home/hpe/oneview-demo/ansible/roles/server-profiles/tasks/profile-template.yaml for localhost included: /home/hpe/oneview-demo/ansible/roles/server-profiles/tasks/profile-template.yaml for localhost TASK [server-profiles : Gather facts about a Enclosure Group by name] ********** ok: [localhost] TASK [server-profiles : Gather facts about Ethernet networks] ****************** [WARNING]: The loop variable 'item' is already in use. You should set the `loop_var` value in the `loop_control` option for the task to something else to avoid variable collisions and unexpected behavior. ok: [localhost] => (item=Prod-101) ok: [localhost] => (item=Prod-102) TASK [server-profiles : Prepare individual JSON objects for Ethernet networks] * [WARNING]: The loop variable 'item' is already in use. You should set the `loop_var` value in the `loop_control` option for the task to something else to avoid variable collisions and unexpected behavior. ok: [localhost] => (item={u'changed': False, '_ansible_no_log': False, '_ansible_item_result': True, 'item': u'Prod-101', 'invocation': {'module_name': u'oneview_ethernet_network_facts', u'module_args': {u'config': u'oneview-config.json', u'name': u'Prod-101'}}, u'ansible_facts': {u'ethernet_networks': [{u'status': u'OK', u'category': u'ethernet-networks', u'ethernetNetworkType': u'Tagged', u'description': None, u'name': u'Prod-101', u'created': u'2016-08-23T05:17:05.557Z', u'uri': u'/rest/ethernet-networks/a1021dc9-f76d-4bc8-bfb8-ceaa49591477', u'vlanId': 101, u'modified': u'2016-08-23T05:17:05.558Z', u'fabricUri': u'/rest/fabrics/a915e022-7ebb-4add-ad14-d88ef088d421', u'eTag': u'c580dc99-6d4f-4b41-aec3-a04c4a987b98', u'purpose': u'General', u'state': u'Active', u'connectionTemplateUri': u'/rest/connection-templates/6386824a-6090-431c-b8c2-fc4d5e711cd7', u'type': u'ethernet-networkV3', u'smartLink': True, u'privateNetwork': False}]}}) ok: [localhost] => (item={u'changed': False, '_ansible_no_log': False, '_ansible_item_result': True, 'item': u'Prod-102', 'invocation': {'module_name': u'oneview_ethernet_network_facts', u'module_args': {u'config': u'oneview-config.json', u'name': u'Prod-102'}}, u'ansible_facts': {u'ethernet_networks': [{u'status': u'OK', u'category': u'ethernet-networks', u'ethernetNetworkType': u'Tagged', u'description': None, u'name': u'Prod-102', u'created': u'2016-08-23T05:17:06.091Z', u'uri': u'/rest/ethernet-networks/d49d4a7d-2a0f-4534-a9e3-622e8844ab19', u'vlanId': 102, u'modified': u'2016-08-23T05:17:06.093Z', u'fabricUri': u'/rest/fabrics/a915e022-7ebb-4add-ad14-d88ef088d421', u'eTag': u'ac665bd4-5fa1-40b4-a1fc-eef273230c9f', u'purpose': u'General', u'state': u'Active', u'connectionTemplateUri': u'/rest/connection-templates/a8914e9b-51ba-4e22-a611-d45ce19ef224', u'type': u'ethernet-networkV3', u'smartLink': True, u'privateNetwork': False}]}}) TASK [server-profiles : Map Ethernet configurations to JSON array] ************* ok: [localhost] TASK [server-profiles : Ensure server profile template is present] ************* changed: [localhost] TASK [server-profiles : Gather facts about a Enclosure Group by name] ********** ok: [localhost] TASK [server-profiles : Gather facts about Ethernet networks] ****************** [WARNING]: The loop variable 'item' is already in use. You should set the `loop_var` value in the `loop_control` option for the task to something else to avoid variable collisions and unexpected behavior. ok: [localhost] => (item=Dev-103) TASK [server-profiles : Prepare individual JSON objects for Ethernet networks] * [WARNING]: The loop variable 'item' is already in use. You should set the `loop_var` value in the `loop_control` option for the task to something else to avoid variable collisions and unexpected behavior. ok: [localhost] => (item={u'changed': False, '_ansible_no_log': False, '_ansible_item_result': True, 'item': u'Dev-103', 'invocation': {'module_name': u'oneview_ethernet_network_facts', u'module_args': {u'config': u'oneview-config.json', u'name': u'Dev-103'}}, u'ansible_facts': {u'ethernet_networks': [{u'status': u'OK', u'category': u'ethernet-networks', u'ethernetNetworkType': u'Tagged', u'description': None, u'name': u'Dev-103', u'created': u'2016-08-23T05:17:06.590Z', u'uri': u'/rest/ethernet-networks/edbfc0b8-0b35-4316-a247-9790ec69d0ef', u'vlanId': 103, u'modified': u'2016-08-23T05:17:06.591Z', u'fabricUri': u'/rest/fabrics/a915e022-7ebb-4add-ad14-d88ef088d421', u'eTag': u'c9deae72-42e3-4899-857a-325488eaca90', u'purpose': u'General', u'state': u'Active', u'connectionTemplateUri': u'/rest/connection-templates/7b30af86-5829-41df-baf4-0b2d44062962', u'type': u'ethernet-networkV3', u'smartLink': True, u'privateNetwork': False}]}}) TASK [server-profiles : Map Ethernet configurations to JSON array] ************* ok: [localhost] TASK [server-profiles : Ensure server profile template is present] ************* changed: [localhost] TASK [server-profiles : Ensure server profiles are present] ******************** ok: [localhost] => (item={u'profile': u'DB_servers', u'name': u'My_DB_1'}) ok: [localhost] => (item={u'profile': u'DB_servers', u'name': u'My_DB_2'}) ok: [localhost] => (item={u'profile': u'APP_servers', u'name': u'My_APP_1'}) ok: [localhost] => (item={u'profile': u'APP_servers', u'name': u'My_APP_2'}) PLAY RECAP ********************************************************************* 192.168.56.10 : ok=10 changed=6 unreachable=0 failed=0 localhost : ok=28 changed=5 unreachable=0 failed=0
Za pár minut jsme nastavili něco, co by trvalo naklikávat dost dlouhou dobu. Vezměte v úvahu, že přidat síť nebo šablonu profilu či profil je teď pro nás otázka napsat jeden či dva řádky do textu a spustit Ansible znovu. Dále máme velmi dobrý přehled a soubor s konfigurací můžeme držet ve verzovacím systému. Pokud si něco rozbijeme, spustíme Ansible a ten zjistí nesrovnalosti skutečného a požadovaného stavu a situaci napraví. A v neposlední řadě vám už dnes prozradím, že nastavení externí sítě něbylo z OneView (to takovou funkci má pouze pro vnitřní interconnect moduly v blade systému), ale Ansible použil HPE Comware moduly (a stejně tak můžete konfigurovat třeba OpenSwitch, Aristu a mnohé další). Podařilo se nám do jednoho souboru spojit znalosti serveraře a síťaře. Přidám síť tady a ona se dostane do obou světů.