W poprzednich dwóch krokach sprawdziłem warunki, czy w ogóle mogę odtworzyć Cisco ISE z kopii zapasowej. Sprawdziłem czy mam “świeżą” kopię zapasową oraz czas licencji trial jest bliski wyczerpania. Jeżeli oba warunki są spełnione to mogę przechodzę do odtworzenia maszyny z kopii zapasowej. Na początek jednak muszę wyłączyć działającą maszynę wirtualną z Cisco ISE i na wszelki wypadek zrobić jej kopię zapasową na poziomie samego hypervisora.
Spis treści
- Część I: Opis projektu
- Część II: Przygotowanie środowiska Jenkins i Ansible
- Część III: Sprawdzamy datę ostatniego backupu
- Część IV: Sprawdzamy ważność licencji trial na Cisco ISE
- Część V: Zatrzymujemy ISE i wyłączamy maszynę wirtualną
Wyłączenie maszyny wirtualnej z Cisco ISE
Wyłączenie jakiejkolwiek maszyny wirtualnej czy fizycznego serwera przez odcięcie wirtualnego lub rzeczywistego zasilania nigdy nie jest dobrym pomysłem. Może to powodować utratę danych i błędy w logicznej strukturze dysków, które mogą uniemożliwić późniejsze ponowne uruchomienie serwera lub usług. Na wszelki wypadek, gdyby coś poszło nie tak, chcę mieć kopię całej maszyny wirtualnej bezpośrednio na serwerze, na której jest ona uruchamiana. Zatem muszę poprawnie zamknąć system aby uniknąć potencjalnych błędów.
Aby wyłączyć Cisco ISE muszę mieć dostęp do konsoli serwera za pomocą protokołu SSH. Warto w tym miejscu przypomnieć że konta do interfejsu GUI i konta do konsoli to dwie odmienne rzeczy. Fakt że mam dostęp do panelu administracyjnego GUI nie oznacza że mam jednocześnie dostęp do konsoli. Dobrą praktyką jest także aby konta interfejsu web i konta lokalne były od siebie oddzielone. Nawet jeżeli mają tę samą nazwę użytkownika, to powinniśmy im nadać inne hasła.
Gdy już posiadam dostęp do konsoli Cisco ISE pozostaje zaprogramowanie logiki samego procesu wyłączenia serwera. Ja preferuję robienie tego w dwóch krokach:
- W pierwszym kroku wyłączam samą usługę Cisco ISE. Ten proces nie jest szybki, może trwać nawet 30 minut lub dłużej. Jeżeli zatrzymam najpierw usługę to mam pewność, że dane zgromadzone w Cisco ISE są poprawnie zapisane, a sama usługa zatrzymana.
- W drugim kroku wyłączam sam serwer. Mam gwarancję, że system plików pozostanie nieuszkodzony.
Zatrzymanie usługi Cisco ISE
Praca z konsolą Cisco ISE w skryptach Ansible nie jest trudna. W niczym nie odbiega ona od pracy z konsolą serwera Linux, używam nawet tych samych modułów Ansible. Jest jednak jeden haczyk. Praca z konsolą Cisco ISE przypomina pracę z konsolą bash czy sh Linuksa. Wydajemy nawet, obok poleceń typowych dla ISE, polecenia takie jak w typowym shell-u. Jeżeli jednak nie skonfigurujemy parametru ansible_network_os tak jakby było to router Cisco IOS, to komunikacja nie będzie działać poprawnie. W szczególności polecenia mogą się wtedy poprawnie nie wydawać, gdyż brakuje wciśnięcia wirtualnego “Enter” na końcu. Zatem playbook musimy zacząć od definicji parametrów połączenia z konsolą.
- name: Shutdown Cisco ISE hosts: ise gather_facts: no vars: ansible_become: no # ISE does not have a superuser/enable mode ansible_connection: ansible.netcommon.network_cli ansible_network_os: cisco.ios.ios ansible_command_timeout: 2200
Drugim niezbędnym parametrem jest ansible_command_timeout, który musimy ustawić na wartość większą niż czas wykonania komend, które będziemy wydawać. Jak już wspomniałem zatrzymanie procesu Cisco ISE może trwać kilkadziesiąt minut.
application stop ise
do zatrzymania usługi ISE. Dopuszczę niepowodzenia tego zadania (ignore_errors: true
), aby wyłapać potencjalne błędy. Następnie, w zadaniu „Check ISE Application Server Status” sprawdzę status serwera aplikacji ISE, używając komendy show application status ise
. Sprawdzanie statusu będzie kontynuowane, dopóki nie uzyskam potwierdzenia w wyjściu, że serwer aplikacji nie działa. Zadanie to wykona się maksymalnie 60 razy co 60 sekund. Dopuszczam również niepowodzenia tego zadania. tasks: - name: Stop ISE service ansible_command_timeout: 2200 ansible.netcommon.cli_command: command: application stop ise ignore_errors: true - name: Check ISE Application Server Status ansible.netcommon.cli_command: command: "show application status ise" register: result until: "'Application Server not running' in result.stdout" retries: 60 delay: 60 ignore_errors: true
W przypadku obu zadań parametry powinniśmy dostosować się do własnych potrzeb. Także kwestie ignorowania potencjalnych błędów w danym zadaniu muszą odzwierciedlać w pełni realizowane przez Ciebie zadanie. Pamiętaj, że prezentuję tutaj tylko przykłady, które są odpowiednie dla mnie. Każdy fragment kodu powinien być dostosowany do Twoich potrzeb.
Wyłączenie serwera Cisco ISE
W następnym kroku przystępuję do zadania „Halt current ISE instance”, gdzie wydam komendę halt
w celu zatrzymania działającej instancji ISE. Zrób nasz która potencjalnie pojawia się na tym etapie to konieczność odpowiedzenia na pytanie które będzie zadane na naszej konsoli . Na szczęście moduł ansible.netcommon.cli_command
umożliwia interakcję na konsoli. Działa ona w ten sposób, że programujemy frazy, które będą wyszukiwane w wyświetlanym tekście i definiujemy dane, które mają wtedy zostać na konsoli wprowadzone. W odpowiedzi na monit, potwierdzę kontynuację zamknięcia wpisując „y”. Ponieważ ISE w wersji 3.2 może się nie zamknąć poprawnie, jeśli limit czasu wynosi mniej niż około 7 minut, ustawiam limit czasu na 600 sekund. Zmieniam w tym zadaniu lokalnie wartość timeout względem globalnego parametru ustawionego na początku playbooka. Rejestruję też wynik wykonania zadania by go potem wyświetlić w logach
- name: Halt current ISE instance vars: # ⚠SE 3.2: A command timeout of < ~7 minutes will cause a failure! ansible_command_timeout: 600 ansible.netcommon.cli_command: command: halt check_all: yes # run all of the commands prompt: - Continue with shutdown answer: - "y" register: output - ansible.builtin.debug: msg: "{{ output }}"
Wyłączenie maszyny wirtualnej Cisco ISE nastąpi samoczynnie. Nie musimy dodatkowo wyłączać jej z poziomu hypervisora.
Kopia zapasowa na ESXi
Kolekcja community.vmware
to zestaw modułów, wtyczek i ról utrzymywanych przez społeczność, które umożliwiają zarządzanie różnymi aspektami infrastruktury VMware. Dostępne są moduły do zarządzania maszynami wirtualnymi, sieciami, składami danych, folderami, zasadami tagowania i wieloma innymi elementami w środowisku VMware vSphere. Działają one zarówno z zaistalowanym vCenter jak i bezpośrednio na hypervisorach ESXi. Do korzystania z modułów z tej kolekcji wymagane jest posiadanie odpowiednich bibliotek Pythona, takich jak PyVmomi.
Mój playbook ma zdefiniowane trzy zadania. Rozpocznę od wykonania „Get current date”, gdzie uruchomię komendę date "+%Y%m%d"
na lokalnym hoście (delegate_to: localhost
). Komenda ta zwróci bieżącą datę w formacie YYYYMMDD, którą zarejestruję pod nazwą current_date. Umożliwi mi to wykorzystanie tej daty w nowej nazwie VM.
- name: Get current date command: date "+%Y%m%d" delegate_to: localhost register: current_date
W następnym zadaniu wykorzystam moduł community.vmware.vmware_guest_info
do zebrania informacji o maszynie wirtualnej. Podam szczegóły, takie jak nazwa hosta, nazwa użytkownika, hasło oraz lokalizację datacenter i folderu, w którym znajduje się VM. Informacje te pobiorę dla maszyny o nazwie określonej w zmiennej vm_name
. Zawiera ona nazwę DNS mojej instancji ISE. Przekazuję ją jako parametr wywołania w Jenkins. Zebrane dane zapiszę w zmiennej vm_info
i pozwolę na ignorowanie ewentualnych błędów w tym zadaniu. Zadanie to także zostanie wykonane na moim lokalnym hoście.
- name: Gather VM info community.vmware.vmware_guest_info: hostname: '{{ inventory_hostname }}' username: '{{ esxi_username }}' password: '{{ esxi_password }}' validate_certs: no datacenter: ha-datacenter folder: "/vm" name: '{{ vm_name }}' register: vm_info ignore_errors: true delegate_to: localhost
W ostatnim kroku użyję modułu community.vmware.vmware_guest
do zmiany nazwy maszyny wirtualnej. Nowa nazwa będzie zawierała prefiks “ise-lab-“, datę zwróconą przez wcześniejsze zadanie, oraz sufiks “-backup.virl.lan”. Operację tę wykonam tylko wtedy, gdy UUID instancji VM jest zdefiniowany (co jest sprawdzane warunkiem when: vm_info.instance.instance_uuid is defined
). Podobnie jak wcześniej, wykonam to zadanie na moim lokalnym hoście.
- name: Rename VM if it exists community.vmware.vmware_guest: hostname: '{{ inventory_hostname }}' username: '{{ esxi_username }}' password: '{{ esxi_password }}' validate_certs: no uuid: "{{ vm_info.instance.hw_product_uuid }}" name: "ise-lab-{{ current_date.stdout }}-backup.virl.lan" when: vm_info.instance.instance_uuid is defined delegate_to: localhost
Poprawne wykonanie tego playbooka spowouje zmianę nazwy obecnej maszyny wirtualnej.
Jenkins pipeline
Na koniec zostawiam fragment kodu Jenkinsfile służący do uruchomienia tych dwóch playbooków. Pamiętajmy że niepowodzenie wykonania któregokolwiek z nich zakończy błędem działanie całego potoku.
stage('Halt current ISE server') { environment { ise_ssh_username = credentials('ise_ssh_username') ise_ssh_password = credentials('ise_ssh_password') } steps { ansiblePlaybook( playbook: 'ise-shutdown.yaml', inventory: 'inventory-ise', extraVars: [ ansible_ssh_user: env.ise_ssh_username, ansible_ssh_password: env.ise_ssh_password, ] ) } } stage('Rename current VM and save it as a backup') { environment { esxi_username = credentials('esxi_username') esxi_password = credentials('esxi_password') } steps { ansiblePlaybook( playbook: 'ise-vm-rename.yaml', inventory: 'inventory-esxi', extraVars: [ esxi_username: env.esxi_username, esxi_password: env.esxi_password, vm_name: params.vm_name, ] ) } }