- 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ů.
