Add cloud and patching demos (#9)
Add demos for cloud provisioning and patching
1
.gitignore
vendored
@@ -6,3 +6,4 @@ choose_demo_example_azure.yml
|
||||
choose_demo_example_aws.yml
|
||||
.ansible.cfg
|
||||
*.gz
|
||||
|
||||
|
||||
2
ansible.cfg
Normal file
@@ -0,0 +1,2 @@
|
||||
[defaults]
|
||||
collections_paths=./collections
|
||||
34
cloud/README.md
Normal file
@@ -0,0 +1,34 @@
|
||||
# Ansible Cloud Demos
|
||||
|
||||
## Setup
|
||||
> These steps may differ if you in your environment
|
||||
|
||||
### Add AWS Credentials
|
||||
|
||||
1) Add AWS Access and Secret key to the AWS Credential created by the setup job
|
||||
|
||||
### Add Workshop Credential Password
|
||||
|
||||
1) Add the password used to login to Controller. This allows you to connect to Windows Servers provisioned with Create VM job. Required until [RFE](https://github.com/ansible/workshops/issues/1597]) is complete
|
||||
|
||||
### Remove Inventory Variables
|
||||
|
||||
1) Remove Workshop Inventory variables on the Details page of the inventory. Required until [RFE](https://github.com/ansible/workshops/issues/1597]) is complete
|
||||
|
||||
### Getting your Puiblic Key for Create Infra Job
|
||||
|
||||
1) Connect to the command line of your Controller server. This is easiest to do by opening the VS Code Web Editor from the landing page where you found the Controller login details.
|
||||
2) Open a Terminal Window in the VS Code Web Editor.
|
||||
3) SSH to one of your linux nodes (eg. `ssh node1`). This should log you into the node as `ec2-user`
|
||||
4) `cat .ssh/authorized_keys` and copy the key listed including the `ssh-rsa` prefix
|
||||
|
||||
|
||||
## Demos
|
||||
|
||||
### Cloud / Create Infra
|
||||
|
||||
The Create Infra job builds cloud infrastructure based on the provider definition in the included `demo.cloud` collection.
|
||||
|
||||
### Cloud / Create VM
|
||||
|
||||
The Create VM job builds a VM in the given provider based on the included `demo.cloud` collection. VM blueprints define variables for each provider that override the collection roles.
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
vm_blueprint_providers:
|
||||
- aws
|
||||
aws_instance_filter: 'Windows_Server-2019-English-Core-Base*'
|
||||
aws_image_filter: 'Windows_Server-2019-English-Core-Base*'
|
||||
aws_instance_size: t3.medium
|
||||
aws_userdata_template: aws_windows_userdata
|
||||
15
cloud/destroy_vm.yml
Normal file
@@ -0,0 +1,15 @@
|
||||
---
|
||||
- hosts: "{{ HOSTS }}"
|
||||
gather_facts: no
|
||||
|
||||
tasks:
|
||||
- name: list systems to be destroyed
|
||||
pause:
|
||||
seconds: 30
|
||||
prompt: "{{ inventory_hostname }} will be DESTROYED in 30 seconds. Cancel the job to Abort."
|
||||
|
||||
- name: destroy vm
|
||||
include_role:
|
||||
name: "demo.cloud.aws"
|
||||
tasks_from: destroy_vm
|
||||
when: "'cloud_aws' in group_names"
|
||||
@@ -0,0 +1,24 @@
|
||||
---
|
||||
#######
|
||||
# AWS VARS
|
||||
#######
|
||||
aws_vpc_name: ansible
|
||||
aws_vpc_prefix: demo
|
||||
aws_vpc_cidr_block: 10.0.0.0/16
|
||||
aws_subnet_cidr: 10.0.1.0/24
|
||||
aws_region: us-east-1
|
||||
aws_vm_name: "{{ vm_name }}"
|
||||
aws_vm_owner: "{{ vm_owner }}"
|
||||
aws_blueprint: "{{ vm_blueprint }}"
|
||||
#aws_image_filter: "{{ omit }}"
|
||||
#aws_instance_size: "{{ omit }}"
|
||||
#aws_image_architecture: "{{ omit }}"
|
||||
#aws_image_owners: "{{ omit }} "
|
||||
aws_userdata_template: default
|
||||
aws_keypair_name: "{{ aws_vpc_name }}-{{ aws_vpc_prefix }}-demo-key"
|
||||
aws_securitygroup_name: "{{ aws_vpc_name }}-{{ aws_vpc_prefix }}-sec-group"
|
||||
aws_env_tag: prod
|
||||
aws_purpose_tag: ansible_demo
|
||||
aws_ansiblegroup_tag: cloud
|
||||
aws_ec2_wait: true
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
---
|
||||
- name: AWS | CREATE INFRA | vpc
|
||||
amazon.aws.ec2_vpc_net:
|
||||
state: present
|
||||
name: "{{ aws_vpc_name }}-{{ aws_vpc_prefix }}-vpc"
|
||||
cidr_block: "{{ aws_vpc_cidr_block }}"
|
||||
tenancy: default
|
||||
region: "{{ aws_region }}"
|
||||
tags:
|
||||
owner: "{{ aws_vpc_name }}"
|
||||
purpose: "{{ aws_purpose_tag }}"
|
||||
register: aws_vpc
|
||||
|
||||
- name: AWS | CREATE INFRA | internet gateway
|
||||
amazon.aws.ec2_vpc_igw:
|
||||
state: present
|
||||
vpc_id: "{{ aws_vpc.vpc.id }}"
|
||||
region: "{{ aws_region }}"
|
||||
tags:
|
||||
Name: "{{ aws_vpc_name }}-{{aws_vpc_prefix }}-vpc-igw"
|
||||
owner: "{{ aws_vpc_name }}"
|
||||
purpose: "{{ aws_purpose_tag }}"
|
||||
register: aws_gateway
|
||||
|
||||
- name: Create security group internal
|
||||
amazon.aws.ec2_group:
|
||||
state: present
|
||||
name: "{{ aws_vpc_name }}-{{aws_vpc_prefix }}-sec-group"
|
||||
region: "{{ aws_region }}"
|
||||
description: Inbound WinRM and RDP, http for demo servers and internal AD ports
|
||||
rules:
|
||||
- proto: tcp
|
||||
ports:
|
||||
- 80 # HTTP
|
||||
- 443 # HTTPS
|
||||
- 22 # SSH
|
||||
- 5986 # WinRM
|
||||
- 3389 # RDP
|
||||
cidr_ip: 0.0.0.0/0
|
||||
- proto: icmp
|
||||
to_port: -1
|
||||
from_port: -1
|
||||
cidr_ip: 0.0.0.0/0
|
||||
- proto: tcp
|
||||
ports:
|
||||
- 80 # HTTP
|
||||
- 5986 # WinRM
|
||||
- 3389 # RDP
|
||||
- 53 # DNS
|
||||
- 88 # Kerberos Authentication
|
||||
- 135 # RPC
|
||||
- 139 # Netlogon
|
||||
- 389 # LDAP
|
||||
- 445 # SMB
|
||||
- 464 # Kerberos Authentication
|
||||
- 5432 # PostgreSQL
|
||||
- 636 # LDAPS (LDAP over TLS)
|
||||
- 873 # Rsync
|
||||
- 3268-3269 # Global Catalog
|
||||
- 1024-65535 # Ephemeral RPC ports
|
||||
cidr_ip: 10.0.0.0/16
|
||||
- proto: udp
|
||||
ports:
|
||||
- 53 # DNS
|
||||
- 88 # Kerberos Authentication
|
||||
- 123 # NTP
|
||||
- 137-138 # Netlogon
|
||||
- 389 # LDAP
|
||||
- 445 # SMB
|
||||
- 464 # Kerberos Authentication
|
||||
- 1024-65535 # Ephemeral RPC ports
|
||||
cidr_ip: 10.0.0.0/16
|
||||
rules_egress:
|
||||
- proto: -1
|
||||
cidr_ip: 0.0.0.0/0
|
||||
vpc_id: "{{ aws_vpc.vpc.id }}"
|
||||
tags:
|
||||
Name: "{{ aws_vpc_name }}-{{aws_vpc_prefix }}-sec-group"
|
||||
owner: "{{ aws_vpc_name }}"
|
||||
purpose: "{{ aws_purpose_tag }}"
|
||||
|
||||
- name: Create a subnet on the VPC
|
||||
amazon.aws.ec2_vpc_subnet:
|
||||
state: present
|
||||
vpc_id: "{{ aws_vpc.vpc.id }}"
|
||||
cidr: "{{ aws_subnet_cidr }}"
|
||||
region: "{{ aws_region }}"
|
||||
map_public: yes
|
||||
tags:
|
||||
Name: "{{ aws_vpc_name }}-{{aws_vpc_prefix }}-subnet"
|
||||
owner: "{{ aws_vpc_name }}"
|
||||
purpose: "{{ aws_purpose_tag }}"
|
||||
register: aws_subnet
|
||||
|
||||
- name: Create a subnet route table
|
||||
amazon.aws.ec2_vpc_route_table:
|
||||
state: present
|
||||
vpc_id: "{{ aws_vpc.vpc.id }}"
|
||||
region: "{{ aws_region }}"
|
||||
subnets:
|
||||
- "{{ aws_subnet.subnet.id }}"
|
||||
routes:
|
||||
- dest: 0.0.0.0/0
|
||||
gateway_id: "{{ aws_gateway.gateway_id }}"
|
||||
tags:
|
||||
Name: "{{ aws_vpc_name }}-{{aws_vpc_prefix }}-vpc-rtbl"
|
||||
owner: "{{ aws_vpc_name }}"
|
||||
purpose: "{{ aws_purpose_tag }}"
|
||||
|
||||
- name: Create AWS keypair
|
||||
amazon.aws.ec2_key:
|
||||
name: "{{ aws_vpc_name }}-{{aws_vpc_prefix }}-demo-key"
|
||||
region: "{{ aws_region }}"
|
||||
key_material: "{{ aws_public_key }}"
|
||||
state: present
|
||||
tags:
|
||||
owner: "{{ aws_vpc_name }}"
|
||||
purpose: "{{ aws_purpose_tag }}"
|
||||
@@ -0,0 +1,47 @@
|
||||
---
|
||||
- name: AWS | CREATE VM | get subnet info
|
||||
amazon.aws.ec2_vpc_subnet_info:
|
||||
region: "{{ aws_region }}"
|
||||
filters:
|
||||
"tag:Name": "{{ aws_vpc_name }}-{{ aws_vpc_prefix }}-subnet"
|
||||
register: aws_subnet
|
||||
|
||||
- name: AWS | CREATE VM | save subnet id
|
||||
set_fact:
|
||||
aws_subnet_id: "{{ aws_subnet.subnets|map(attribute='id')| list | last }}"
|
||||
|
||||
- name: AWS| CREATE VM | find ami
|
||||
amazon.aws.ec2_ami_info:
|
||||
region: "{{ aws_region }}"
|
||||
owners: "{{ aws_image_owners | default(omit)}}"
|
||||
filters:
|
||||
name: "{{ aws_image_filter }}"
|
||||
architecture: "{{ aws_image_architecture | default(omit) }}"
|
||||
register: amis
|
||||
|
||||
- name: AWS| CREATE VM | save ami
|
||||
set_fact:
|
||||
aws_instance_ami: >
|
||||
{{ amis.images | selectattr('name', 'defined') | sort(attribute='creation_date') | last }}
|
||||
|
||||
- name: AWS| CREATE VM | create instance
|
||||
amazon.aws.ec2_instance:
|
||||
network:
|
||||
assign_public_ip: yes
|
||||
key_name: "{{ aws_keypair_name }}"
|
||||
instance_type: "{{ aws_instance_size }}"
|
||||
image_id: "{{ aws_instance_ami.image_id }}"
|
||||
region: "{{ aws_region }}"
|
||||
security_group: "{{ aws_securitygroup_name }}"
|
||||
tags:
|
||||
blueprint: "{{ aws_blueprint }}"
|
||||
purpose: "{{ aws_purpose_tag }}"
|
||||
env: "{{ aws_env_tag }}"
|
||||
ansible_group: "{{ aws_ansiblegroup_tag }}"
|
||||
owner: "{{ aws_vm_owner }}"
|
||||
info: "This instance was built by Red Hat Product Demos"
|
||||
Name: "{{ aws_vm_name }}"
|
||||
wait: "{{ aws_ec2_wait }}"
|
||||
vpc_subnet_id: "{{ aws_subnet_id }}"
|
||||
user_data: "{{ lookup('template', aws_userdata_template+'.j2', template_vars=dict(aws_vm_name=vm_name)) }}"
|
||||
register: aws_vm_output
|
||||
@@ -0,0 +1,7 @@
|
||||
---
|
||||
- name: Destroy VM
|
||||
amazon.aws.ec2_instance:
|
||||
state: absent
|
||||
instance_ids: "{{ instance_id }}"
|
||||
region: "{{ placement.region }}"
|
||||
delegate_to: localhost
|
||||
@@ -0,0 +1,29 @@
|
||||
<powershell>
|
||||
# Disable .Net Optimization Service
|
||||
Get-ScheduledTask *ngen* | Disable-ScheduledTask
|
||||
|
||||
# Disable Windows Auto Updates
|
||||
# https://docs.aws.amazon.com/AWSEC2/latest/WindowsGuide/troubleshooting-windows-instances.html#high-cpu-issue
|
||||
reg add "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update" /v AUOptions /t REG_DWORD /d 1 /f
|
||||
net stop wuauserv
|
||||
net start wuauserv
|
||||
|
||||
# Remove policies stopping us from enabling WinRM
|
||||
reg delete "HKLM\SOFTWARE\Policies\Microsoft\Windows\WinRM\Service" /v AllowBasic /f
|
||||
reg delete "HKLM\SOFTWARE\Policies\Microsoft\Windows\WinRM\Service" /v AllowUnencryptedTraffic /f
|
||||
reg delete "HKLM\SOFTWARE\Policies\Microsoft\Windows\WinRM\Service" /v DisableRunAs /f
|
||||
|
||||
# Disable Windows Defender Monitoring
|
||||
Set-MpPreference -DisableRealtimeMonitoring $true
|
||||
|
||||
# Enable WinRM
|
||||
Invoke-WebRequest -Uri https://raw.githubusercontent.com/ansible/ansible/devel/examples/scripts/ConfigureRemotingForAnsible.ps1 -OutFile C:\ConfigureRemotingForAnsible.ps1
|
||||
C:\ConfigureRemotingForAnsible.ps1 -ForceNewSSLCert -EnableCredSSP
|
||||
|
||||
# add ec2-user
|
||||
$Password = ConvertTo-SecureString {{ ansible_password }} -AsPlainText -Force
|
||||
New-LocalUser -Name "ec2-user" -Description "Ansible Service Account" -Password $Password
|
||||
Add-LocalGroupMember -Group "Administrators" -Member "ec2-user"
|
||||
|
||||
Rename-Computer -NewName {{ aws_vm_name }} -Force -Restart
|
||||
</powershell>
|
||||
@@ -0,0 +1,9 @@
|
||||
---
|
||||
##############
|
||||
# Azure Vars
|
||||
##############
|
||||
az_region:
|
||||
az_rg_name: ansible
|
||||
az_rg_prefix: demo
|
||||
az_vnet_cidr_block: 10.0.0.0/16
|
||||
az_subnet_cidr: 10.0.1.0/24
|
||||
@@ -0,0 +1,76 @@
|
||||
---
|
||||
- name: AZURE | CREATE INFRA | resource group
|
||||
azure.azcollection.azure_rm_resourcegroup:
|
||||
name: "{{ az_rg_name }}-{{ az_rg_prefix }}-rg"
|
||||
location: "{{ az_region }}"
|
||||
|
||||
- name: AZURE | CREATE INFRA | virtual network
|
||||
azure.azcollection.azure_rm_virtualnetwork:
|
||||
resource_group: "{{ az_rg_name }}-{{ az_rg_prefix }}-rg"
|
||||
name: "{{ az_rg_name }}-{{ az_rg_prefix }}-vnet"
|
||||
address_prefixes: "{{ az_vnet_cidr }}"
|
||||
|
||||
- name: AZURE | CREATE INFRA | subnet
|
||||
azure.azcollection.azure_rm_subnet:
|
||||
resource_group: "{{ az_rg_name }}-{{ az_rg_prefix }}-rg"
|
||||
name: "{{ az_rg_name }}-{{ az_rg_prefix }}-subnet }}"
|
||||
address_prefix: "{{ az_subnet_cidr }}"
|
||||
virtual_network: "{{ az_rg_name }}-{{ az_rg_prefix }}-vnet"
|
||||
|
||||
- name: AZURE | CREATE INFRA | security group
|
||||
azure.azcollection.azure_rm_securitygroup:
|
||||
resource_group: "{{ az_rg_name }}-{{ az_rg_prefix }}-rg"
|
||||
name: "{{ az_rg_name }}-{{ az_rg_prefix }}-sec-group"
|
||||
rules:
|
||||
- name: External
|
||||
protocol: Tcp
|
||||
destination_port_range:
|
||||
- 80 # HTTP
|
||||
- 443 # HTTPS
|
||||
- 5986 # WinRM
|
||||
- 3389 # RDP
|
||||
access: Allow
|
||||
priority: 1001
|
||||
direction: Inbound
|
||||
- name: Ping
|
||||
protocol: Icmp
|
||||
access: Allow
|
||||
priority: 1002
|
||||
direction: Inbound
|
||||
- name: Internal TCP
|
||||
protocol: Tcp
|
||||
destination_port_range:
|
||||
- 80 # HTTP
|
||||
- 5986 # WinRM
|
||||
- 3389 # RDP
|
||||
- 53 # DNS
|
||||
- 88 # Kerberos Authentication
|
||||
- 135 # RPC
|
||||
- 139 # Netlogon
|
||||
- 389 # LDAP
|
||||
- 445 # SMB
|
||||
- 464 # Kerberos Authentication
|
||||
- 5432 # PostgreSQL
|
||||
- 636 # LDAPS (LDAP over TLS)
|
||||
- 873 # Rsync
|
||||
- 3268-3269 # Global Catalog
|
||||
- 1024-65535 # Ephemeral RPC ports
|
||||
access: Allow
|
||||
priority: 1003
|
||||
direction: Inbound
|
||||
source_address_prefix: "{{ az_vnet_cidr_block }}"
|
||||
- name: Internal UDP
|
||||
protocol: Udp
|
||||
destination_port_range:
|
||||
- 53 # DNS
|
||||
- 88 # Kerberos Authentication
|
||||
- 123 # NTP
|
||||
- 137-138 # Netlogon
|
||||
- 389 # LDAP
|
||||
- 445 # SMB
|
||||
- 464 # Kerberos Authentication
|
||||
- 1024-65535 # Ephemeral RPC ports
|
||||
access: Allow
|
||||
priority: 1004
|
||||
direction: Inbound
|
||||
source_address_prefix: "{{ az_vnet_cidr_block }}"
|
||||
@@ -0,0 +1,97 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
from ansible.module_utils.basic import * # noqa
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: scan_packages
|
||||
short_description: Return installed packages information as fact data
|
||||
description:
|
||||
- Return information about installed packages as fact data
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Example fact output:
|
||||
# host | success >> {
|
||||
# "ansible_facts": {
|
||||
# "packages": [
|
||||
# {
|
||||
# "version": "1.0.6-5",
|
||||
# "source": "apt",
|
||||
# "arch": "amd64",
|
||||
# "name": "libbz2-1.0"
|
||||
# },
|
||||
# {
|
||||
# "version": "2.7.1-4ubuntu1",
|
||||
# "source": "apt",
|
||||
# "arch": "amd64",
|
||||
# "name": "patch"
|
||||
# },
|
||||
# {
|
||||
# "version": "4.8.2-19ubuntu1",
|
||||
# "source": "apt",
|
||||
# "arch": "amd64",
|
||||
# "name": "gcc-4.8-base"
|
||||
# }
|
||||
# ]
|
||||
'''
|
||||
|
||||
|
||||
def rpm_package_list():
|
||||
import rpm
|
||||
trans_set = rpm.TransactionSet()
|
||||
installed_packages = []
|
||||
for package in trans_set.dbMatch():
|
||||
package_details = {
|
||||
'name':package[rpm.RPMTAG_NAME],
|
||||
'version':package[rpm.RPMTAG_VERSION],
|
||||
'release':package[rpm.RPMTAG_RELEASE],
|
||||
'epoch':package[rpm.RPMTAG_EPOCH],
|
||||
'arch':package[rpm.RPMTAG_ARCH],
|
||||
'source':'rpm' }
|
||||
if installed_packages == []:
|
||||
installed_packages = [package_details]
|
||||
else:
|
||||
installed_packages.append(package_details)
|
||||
return installed_packages
|
||||
|
||||
|
||||
def deb_package_list():
|
||||
import apt
|
||||
apt_cache = apt.Cache()
|
||||
installed_packages = []
|
||||
apt_installed_packages = [pk for pk in apt_cache.keys() if apt_cache[pk].is_installed]
|
||||
for package in apt_installed_packages:
|
||||
ac_pkg = apt_cache[package].installed
|
||||
package_details = {
|
||||
'name':package,
|
||||
'version':ac_pkg.version,
|
||||
'arch':ac_pkg.architecture,
|
||||
'source':'apt'}
|
||||
if installed_packages == []:
|
||||
installed_packages = [package_details]
|
||||
else:
|
||||
installed_packages.append(package_details)
|
||||
return installed_packages
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec = dict(os_family=dict(required=True))
|
||||
)
|
||||
ans_os = module.params['os_family']
|
||||
if ans_os in ('RedHat', 'Suse', 'openSUSE Leap'):
|
||||
packages = rpm_package_list()
|
||||
elif ans_os == 'Debian':
|
||||
packages = deb_package_list()
|
||||
else:
|
||||
packages = None
|
||||
|
||||
if packages is not None:
|
||||
results = dict(ansible_facts=dict(packages=packages))
|
||||
else:
|
||||
results = dict(skipped=True, msg="Unsupported Distribution")
|
||||
module.exit_json(**results)
|
||||
|
||||
|
||||
main()
|
||||
@@ -0,0 +1,186 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import re
|
||||
from ansible.module_utils.basic import * # noqa
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: scan_services
|
||||
short_description: Return service state information as fact data
|
||||
description:
|
||||
- Return service state information as fact data for various service management utilities
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- monit: scan_services
|
||||
# Example fact output:
|
||||
# host | success >> {
|
||||
# "ansible_facts": {
|
||||
# "services": {
|
||||
# "network": {
|
||||
# "source": "sysv",
|
||||
# "state": "running",
|
||||
# "name": "network"
|
||||
# },
|
||||
# "arp-ethers.service": {
|
||||
# "source": "systemd",
|
||||
# "state": "stopped",
|
||||
# "name": "arp-ethers.service"
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
'''
|
||||
|
||||
|
||||
class BaseService(object):
|
||||
|
||||
def __init__(self, module):
|
||||
self.module = module
|
||||
self.incomplete_warning = False
|
||||
|
||||
|
||||
class ServiceScanService(BaseService):
|
||||
|
||||
def gather_services(self):
|
||||
services = {}
|
||||
service_path = self.module.get_bin_path("service")
|
||||
if service_path is None:
|
||||
return None
|
||||
initctl_path = self.module.get_bin_path("initctl")
|
||||
chkconfig_path = self.module.get_bin_path("chkconfig")
|
||||
|
||||
# sysvinit
|
||||
if service_path is not None and chkconfig_path is None:
|
||||
rc, stdout, stderr = self.module.run_command("%s --status-all 2>&1 | grep -E \"\\[ (\\+|\\-) \\]\"" % service_path, use_unsafe_shell=True)
|
||||
for line in stdout.split("\n"):
|
||||
line_data = line.split()
|
||||
if len(line_data) < 4:
|
||||
continue # Skipping because we expected more data
|
||||
service_name = " ".join(line_data[3:])
|
||||
if line_data[1] == "+":
|
||||
service_state = "running"
|
||||
else:
|
||||
service_state = "stopped"
|
||||
services[service_name] = {"name": service_name, "state": service_state, "source": "sysv"}
|
||||
|
||||
# Upstart
|
||||
if initctl_path is not None and chkconfig_path is None:
|
||||
p = re.compile('^\s?(?P<name>.*)\s(?P<goal>\w+)\/(?P<state>\w+)(\,\sprocess\s(?P<pid>[0-9]+))?\s*$')
|
||||
rc, stdout, stderr = self.module.run_command("%s list" % initctl_path)
|
||||
real_stdout = stdout.replace("\r","")
|
||||
for line in real_stdout.split("\n"):
|
||||
m = p.match(line)
|
||||
if not m:
|
||||
continue
|
||||
service_name = m.group('name')
|
||||
service_goal = m.group('goal')
|
||||
service_state = m.group('state')
|
||||
if m.group('pid'):
|
||||
pid = m.group('pid')
|
||||
else:
|
||||
pid = None # NOQA
|
||||
payload = {"name": service_name, "state": service_state, "goal": service_goal, "source": "upstart"}
|
||||
services[service_name] = payload
|
||||
|
||||
# RH sysvinit
|
||||
elif chkconfig_path is not None:
|
||||
#print '%s --status-all | grep -E "is (running|stopped)"' % service_path
|
||||
p = re.compile(
|
||||
'(?P<service>.*?)\s+[0-9]:(?P<rl0>on|off)\s+[0-9]:(?P<rl1>on|off)\s+[0-9]:(?P<rl2>on|off)\s+'
|
||||
'[0-9]:(?P<rl3>on|off)\s+[0-9]:(?P<rl4>on|off)\s+[0-9]:(?P<rl5>on|off)\s+[0-9]:(?P<rl6>on|off)')
|
||||
rc, stdout, stderr = self.module.run_command('%s' % chkconfig_path, use_unsafe_shell=True)
|
||||
# Check for special cases where stdout does not fit pattern
|
||||
match_any = False
|
||||
for line in stdout.split('\n'):
|
||||
if p.match(line):
|
||||
match_any = True
|
||||
if not match_any:
|
||||
p_simple = re.compile('(?P<service>.*?)\s+(?P<rl0>on|off)')
|
||||
match_any = False
|
||||
for line in stdout.split('\n'):
|
||||
if p_simple.match(line):
|
||||
match_any = True
|
||||
if match_any:
|
||||
# Try extra flags " -l --allservices" needed for SLES11
|
||||
rc, stdout, stderr = self.module.run_command('%s -l --allservices' % chkconfig_path, use_unsafe_shell=True)
|
||||
elif '--list' in stderr:
|
||||
# Extra flag needed for RHEL5
|
||||
rc, stdout, stderr = self.module.run_command('%s --list' % chkconfig_path, use_unsafe_shell=True)
|
||||
for line in stdout.split('\n'):
|
||||
m = p.match(line)
|
||||
if m:
|
||||
service_name = m.group('service')
|
||||
service_state = 'stopped'
|
||||
if m.group('rl3') == 'on':
|
||||
rc, stdout, stderr = self.module.run_command('%s %s status' % (service_path, service_name), use_unsafe_shell=True)
|
||||
service_state = rc
|
||||
if rc in (0,):
|
||||
service_state = 'running'
|
||||
#elif rc in (1,3):
|
||||
else:
|
||||
if 'root' in stderr or 'permission' in stderr.lower() or 'not in sudoers' in stderr.lower():
|
||||
self.incomplete_warning = True
|
||||
continue
|
||||
else:
|
||||
service_state = 'stopped'
|
||||
service_data = {"name": service_name, "state": service_state, "source": "sysv"}
|
||||
services[service_name] = service_data
|
||||
return services
|
||||
|
||||
|
||||
class SystemctlScanService(BaseService):
|
||||
|
||||
def systemd_enabled(self):
|
||||
# Check if init is the systemd command, using comm as cmdline could be symlink
|
||||
try:
|
||||
f = open('/proc/1/comm', 'r')
|
||||
except IOError:
|
||||
# If comm doesn't exist, old kernel, no systemd
|
||||
return False
|
||||
for line in f:
|
||||
if 'systemd' in line:
|
||||
return True
|
||||
return False
|
||||
|
||||
def gather_services(self):
|
||||
services = {}
|
||||
if not self.systemd_enabled():
|
||||
return None
|
||||
systemctl_path = self.module.get_bin_path("systemctl", opt_dirs=["/usr/bin", "/usr/local/bin"])
|
||||
if systemctl_path is None:
|
||||
return None
|
||||
rc, stdout, stderr = self.module.run_command("%s list-unit-files --type=service | tail -n +2 | head -n -2" % systemctl_path, use_unsafe_shell=True)
|
||||
for line in stdout.split("\n"):
|
||||
line_data = line.split()
|
||||
if len(line_data) != 2:
|
||||
continue
|
||||
if line_data[1] == "enabled":
|
||||
state_val = "running"
|
||||
else:
|
||||
state_val = "stopped"
|
||||
services[line_data[0]] = {"name": line_data[0], "state": state_val, "source": "systemd"}
|
||||
return services
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(argument_spec = dict())
|
||||
service_modules = (ServiceScanService, SystemctlScanService)
|
||||
all_services = {}
|
||||
incomplete_warning = False
|
||||
for svc_module in service_modules:
|
||||
svcmod = svc_module(module)
|
||||
svc = svcmod.gather_services()
|
||||
if svc is not None:
|
||||
all_services.update(svc)
|
||||
if svcmod.incomplete_warning:
|
||||
incomplete_warning = True
|
||||
if len(all_services) == 0:
|
||||
results = dict(skipped=True, msg="Failed to find any services. Sometimes this is due to insufficient privileges.")
|
||||
else:
|
||||
results = dict(ansible_facts=dict(services=all_services))
|
||||
if incomplete_warning:
|
||||
results['msg'] = "WARNING: Could not find status for all services. Sometimes this is due to insufficient privileges."
|
||||
module.exit_json(**results)
|
||||
|
||||
|
||||
main()
|
||||
@@ -0,0 +1,66 @@
|
||||
#!powershell
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# WANT_JSON
|
||||
# POWERSHELL_COMMON
|
||||
|
||||
$uninstall_native_path = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"
|
||||
$uninstall_wow6432_path = "HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall"
|
||||
|
||||
if ([System.IntPtr]::Size -eq 4) {
|
||||
|
||||
# This is a 32-bit Windows system, so we only check for 32-bit programs, which will be
|
||||
# at the native registry location.
|
||||
|
||||
[PSObject []]$packages = Get-ChildItem -Path $uninstall_native_path |
|
||||
Get-ItemProperty |
|
||||
Select-Object -Property @{Name="name"; Expression={$_."DisplayName"}},
|
||||
@{Name="version"; Expression={$_."DisplayVersion"}},
|
||||
@{Name="publisher"; Expression={$_."Publisher"}},
|
||||
@{Name="arch"; Expression={ "Win32" }} |
|
||||
Where-Object { $_.name }
|
||||
|
||||
} else {
|
||||
|
||||
# This is a 64-bit Windows system, so we check for 64-bit programs in the native
|
||||
# registry location, and also for 32-bit programs under Wow6432Node.
|
||||
|
||||
[PSObject []]$packages = Get-ChildItem -Path $uninstall_native_path |
|
||||
Get-ItemProperty |
|
||||
Select-Object -Property @{Name="name"; Expression={$_."DisplayName"}},
|
||||
@{Name="version"; Expression={$_."DisplayVersion"}},
|
||||
@{Name="publisher"; Expression={$_."Publisher"}},
|
||||
@{Name="arch"; Expression={ "Win64" }} |
|
||||
Where-Object { $_.name }
|
||||
|
||||
$packages += Get-ChildItem -Path $uninstall_wow6432_path |
|
||||
Get-ItemProperty |
|
||||
Select-Object -Property @{Name="name"; Expression={$_."DisplayName"}},
|
||||
@{Name="version"; Expression={$_."DisplayVersion"}},
|
||||
@{Name="publisher"; Expression={$_."Publisher"}},
|
||||
@{Name="arch"; Expression={ "Win32" }} |
|
||||
Where-Object { $_.name }
|
||||
|
||||
}
|
||||
|
||||
$result = New-Object psobject @{
|
||||
ansible_facts = New-Object psobject @{
|
||||
packages = $packages
|
||||
}
|
||||
changed = $false
|
||||
}
|
||||
|
||||
Exit-Json $result;
|
||||
@@ -0,0 +1,31 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: win_scan_packages
|
||||
short_description: Return Package state information as fact data
|
||||
description:
|
||||
- Return Package state information as fact data for various Packages
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- monit: win_scan_packages
|
||||
# Example fact output:
|
||||
# host | success >> {
|
||||
# "ansible_facts": {
|
||||
# "packages": [
|
||||
{
|
||||
"name": "Mozilla Firefox 76.0.1 (x64 en-US)",
|
||||
"version": "76.0.1",
|
||||
"publisher": "Mozilla",
|
||||
"arch": "Win64"
|
||||
},
|
||||
{
|
||||
"name": "Mozilla Maintenance Service",
|
||||
"version": "76.0.1",
|
||||
"publisher": "Mozilla",
|
||||
"arch": "Win64"
|
||||
},
|
||||
# }
|
||||
'''
|
||||
@@ -0,0 +1,30 @@
|
||||
#!powershell
|
||||
# This file is part of Ansible
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
# WANT_JSON
|
||||
# POWERSHELL_COMMON
|
||||
|
||||
$result = New-Object psobject @{
|
||||
ansible_facts = New-Object psobject @{
|
||||
services = Get-Service |
|
||||
Select-Object -Property @{Name="name"; Expression={$_."DisplayName"}},
|
||||
@{Name="win_svc_name"; Expression={$_."Name"}},
|
||||
@{Name="state"; Expression={$_."Status".ToString().ToLower()}}
|
||||
}
|
||||
changed = $false
|
||||
}
|
||||
|
||||
Exit-Json $result;
|
||||
@@ -0,0 +1,34 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
DOCUMENTATION = '''
|
||||
---
|
||||
module: win_scan_services
|
||||
short_description: Return service state information as fact data
|
||||
description:
|
||||
- Return service state information as fact data for various service management utilities
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- monit: win_scan_services
|
||||
# Example fact output:
|
||||
# host | success >> {
|
||||
# "ansible_facts": {
|
||||
# "services": [
|
||||
{
|
||||
"name": "AllJoyn Router Service",
|
||||
"win_svc_name": "AJRouter",
|
||||
"state": "stopped"
|
||||
},
|
||||
{
|
||||
"name": "Application Layer Gateway Service",
|
||||
"win_svc_name": "ALG",
|
||||
"state": "stopped"
|
||||
},
|
||||
{
|
||||
"name": "Application Host Helper Service",
|
||||
"win_svc_name": "AppHostSvc",
|
||||
"state": "running"
|
||||
},
|
||||
# }
|
||||
'''
|
||||
@@ -0,0 +1,36 @@
|
||||
build_report_network
|
||||
========
|
||||
|
||||
Installs Apache and creates a report based on facts from network devices
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
Must run on Apache server
|
||||
|
||||
Role Variables / Configuration
|
||||
--------------
|
||||
|
||||
N/A
|
||||
|
||||
Dependencies
|
||||
------------
|
||||
|
||||
N/A
|
||||
|
||||
Example Playbook
|
||||
----------------
|
||||
|
||||
The role can be used to create an html report on any number of Linux hosts using any number of network devices
|
||||
|
||||
|
||||
```
|
||||
---
|
||||
- hosts: all
|
||||
|
||||
tasks:
|
||||
- name: Run Network Report
|
||||
import_role:
|
||||
name: shadowman.reports.build_report_network
|
||||
|
||||
```
|
||||
|
After Width: | Height: | Size: 113 KiB |
@@ -0,0 +1,202 @@
|
||||
p.hostname {
|
||||
color: #000000;
|
||||
font-weight: bolder;
|
||||
font-size: large;
|
||||
margin: auto;
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
#subtable {
|
||||
background: #ebebeb;
|
||||
margin: 0px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#subtable tbody tr td {
|
||||
padding: 5px 5px 5px 5px;
|
||||
}
|
||||
|
||||
#subtable thead th {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
* {
|
||||
-moz-box-sizing: border-box;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
font-family: "Open Sans", "Helvetica";
|
||||
|
||||
}
|
||||
|
||||
a {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
p {
|
||||
color: #ffffff;
|
||||
}
|
||||
h1 {
|
||||
text-align: center;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
body {
|
||||
background:#353a40;
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: separate;
|
||||
background:#fff;
|
||||
@include border-radius(5px);
|
||||
@include box-shadow(0px 0px 5px rgba(0,0,0,0.3));
|
||||
}
|
||||
|
||||
.main_net_table {
|
||||
margin:50px auto;
|
||||
}
|
||||
|
||||
thead {
|
||||
@include border-radius(5px);
|
||||
}
|
||||
|
||||
thead th {
|
||||
font-size:16px;
|
||||
font-weight:400;
|
||||
color:#fff;
|
||||
@include text-shadow(1px 1px 0px rgba(0,0,0,0.5));
|
||||
text-align:left;
|
||||
padding:20px;
|
||||
border-top:1px solid #858d99;
|
||||
background: #353a40;
|
||||
|
||||
&:first-child {
|
||||
@include border-top-left-radius(5px);
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
@include border-top-right-radius(5px);
|
||||
}
|
||||
}
|
||||
|
||||
tbody tr td {
|
||||
font-weight:400;
|
||||
color:#5f6062;
|
||||
font-size:13px;
|
||||
padding:20px 20px 20px 20px;
|
||||
border-bottom:1px solid #e0e0e0;
|
||||
}
|
||||
|
||||
tbody tr:nth-child(2n) {
|
||||
background:#f0f3f5;
|
||||
}
|
||||
|
||||
tbody tr:last-child td {
|
||||
border-bottom:none;
|
||||
&:first-child {
|
||||
@include border-bottom-left-radius(5px);
|
||||
}
|
||||
&:last-child {
|
||||
@include border-bottom-right-radius(5px);
|
||||
}
|
||||
}
|
||||
|
||||
td {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
span.highlight {
|
||||
background-color: yellow;
|
||||
}
|
||||
|
||||
.expandclass {
|
||||
color: #5f6062;
|
||||
}
|
||||
|
||||
.content{
|
||||
display:none;
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
header {
|
||||
width: 100%;
|
||||
position: initial;
|
||||
float: initial;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
border-radius: 0;
|
||||
height: 88px;
|
||||
background-color: #171717;
|
||||
}
|
||||
|
||||
.header-container {
|
||||
margin: 0 auto;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
max-width: 1170px;
|
||||
padding: 0;
|
||||
float: initial;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.header-logo {
|
||||
width: 137px;
|
||||
border: 0;
|
||||
margin: 0;
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
.header-link {
|
||||
margin-left: 40px;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
text-transform: uppercase;
|
||||
font-size: 15px;
|
||||
font-family: 'Red Hat Text';
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.header-link:hover {
|
||||
text-shadow: 0 0 0.02px white;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
table.net_info td {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
p.expandclass:hover {
|
||||
text-decoration: underline;
|
||||
color: #EE0000;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.summary_info {
|
||||
}
|
||||
|
||||
.ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active, a.ui-button:active, .ui-button:active, .ui-button.ui-state-active:hover {
|
||||
border: 1px solid #5F0000;
|
||||
background: #EE0000;
|
||||
}
|
||||
|
||||
div#net_content {
|
||||
padding: 0px;
|
||||
height: auto !important;
|
||||
}
|
||||
|
||||
img.router_image {
|
||||
vertical-align: middle;
|
||||
padding: 0px 10px 10px 10px;
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
table.net_info {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
p.internal_label {
|
||||
color: #000000;
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 24.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Logos" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="930.2px" height="350px" viewBox="0 0 930.2 350" style="enable-background:new 0 0 930.2 350;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFFFFF;}
|
||||
.st1{fill:#EE0000;}
|
||||
</style>
|
||||
<title>Logo-Red_Hat-Ansible_Automation_Platform-A-Reverse-RGB</title>
|
||||
<path class="st0" d="M383.3,228.5h18.8L446,335.7h-17.5l-12.4-31.4h-48l-12.6,31.4h-16.7L383.3,228.5z M410.9,291l-18.7-47l-18.7,47
|
||||
H410.9z"/>
|
||||
<path class="st0" d="M455.2,257.7h15.3v7.8c6.2-6.2,14.7-9.6,23.5-9.3c17.9,0,30.5,12.4,30.5,30.5v49h-15.3v-46.5
|
||||
c0-12.3-7.5-19.8-19.3-19.8c-7.8-0.3-15.1,3.6-19.3,10.1v56.1h-15.3V257.7z"/>
|
||||
<path class="st0" d="M543,315.5c8.1,6.4,16.7,9.8,25.4,9.8c11,0,18.7-4.8,18.7-11.7c0-5.5-4-8.7-12.6-10l-14.1-2
|
||||
c-15.5-2.3-23.3-9.5-23.3-21.6c0-14.1,12.3-23.6,30.5-23.6c11.3-0.1,22.3,3.4,31.5,9.9l-7.8,10.1c-8.6-5.7-16.4-8.1-24.7-8.1
|
||||
c-9.3,0-15.6,4.3-15.6,10.6c0,5.7,3.7,8.4,12.9,9.8l14.1,2c15.5,2.3,23.6,9.7,23.6,21.7c0,14-14.1,24.5-32.6,24.5
|
||||
c-13.5,0-25.6-4-34.2-11.5L543,315.5z"/>
|
||||
<path class="st0" d="M611.6,235.6c0-5.2,4.1-9.4,9.3-9.5c0,0,0,0,0,0c5.2-0.2,9.7,3.9,9.9,9.1c0.2,5.2-3.9,9.7-9.1,9.9
|
||||
c-0.2,0-0.5,0-0.7,0C615.8,245.1,611.6,240.9,611.6,235.6C611.6,235.7,611.6,235.7,611.6,235.6z M628.6,335.7h-15.3v-78h15.3V335.7z
|
||||
"/>
|
||||
<path class="st0" d="M685.5,336.9c-8.5,0-16.8-2.7-23.6-7.8v6.6h-15.2V228.5l15.3-3.4v40c6.6-5.6,15.1-8.7,23.7-8.6
|
||||
c22.1,0,39.4,17.7,39.4,40.1C725.2,319.1,707.9,336.9,685.5,336.9z M662,279.2v35.2c4.9,5.7,13,9.2,21.8,9.2
|
||||
c15,0,26.4-11.5,26.4-26.8c0-15.3-11.5-27-26.4-27C674.9,269.8,667.1,273.2,662,279.2z"/>
|
||||
<path class="st0" d="M755,335.7h-15.3V228.5l15.3-3.4V335.7z"/>
|
||||
<path class="st0" d="M810.5,337.1c-23,0-40.9-17.7-40.9-40.4c0-22.5,17.2-40.1,39.1-40.1c21.5,0,37.7,17.8,37.7,40.8v4.4h-61.6
|
||||
c2,13,13.2,22.5,26.4,22.4c7.2,0.2,14.2-2.3,19.8-6.8l9.8,9.7C832.1,333.7,821.5,337.4,810.5,337.1z M784.9,290.2h46.3
|
||||
c-2.3-11.9-11.5-20.8-22.8-20.8C796.5,269.4,787.2,277.8,784.9,290.2z"/>
|
||||
<path class="st1" d="M202.8,137.5c18.4,0,45.1-3.8,45.1-25.7c0.1-1.7-0.1-3.4-0.5-5l-11-47.7c-2.5-10.5-4.8-15.2-23.2-24.5
|
||||
c-14.3-7.3-45.5-19.4-54.7-19.4c-8.6,0-11.1,11.1-21.3,11.1c-9.8,0-17.1-8.3-26.4-8.3c-8.8,0-14.6,6-19,18.4c0,0-12.4,34.9-14,40
|
||||
c-0.3,0.9-0.4,1.9-0.4,2.9C77.6,92.9,131.1,137.5,202.8,137.5 M250.8,120.7c2.5,12.1,2.5,13.3,2.5,14.9c0,20.6-23.2,32.1-53.7,32.1
|
||||
c-69,0-129.3-40.3-129.3-67c0-3.7,0.8-7.4,2.2-10.8c-24.8,1.3-56.9,5.7-56.9,34c0,46.4,109.9,103.5,196.9,103.5
|
||||
c66.7,0,83.5-30.2,83.5-54C296.1,154.6,279.9,133.4,250.8,120.7"/>
|
||||
<path d="M250.7,120.7c2.5,12.1,2.5,13.3,2.5,14.9c0,20.6-23.2,32.1-53.7,32.1c-69,0-129.3-40.3-129.3-67c0-3.7,0.8-7.4,2.2-10.8
|
||||
l5.4-13.3c-0.3,0.9-0.4,1.9-0.4,2.8c0,13.6,53.5,58.1,125.2,58.1c18.4,0,45.1-3.8,45.1-25.7c0.1-1.7-0.1-3.4-0.5-5L250.7,120.7z"/>
|
||||
<path class="st0" d="M869.1,151.2c0,17.5,10.5,26,29.7,26c5.9-0.1,11.8-1,17.5-2.5v-20.3c-3.7,1.2-7.5,1.7-11.3,1.7
|
||||
c-7.9,0-10.8-2.5-10.8-9.9v-31.1h22.9V94.2h-22.9V67.7l-25,5.4v21.1h-16.6v20.9h16.6L869.1,151.2z M791,151.7
|
||||
c0-5.4,5.4-8.1,13.6-8.1c5,0,10,0.7,14.9,1.9V156c-4.8,2.6-10.2,3.9-15.6,3.9C795.9,159.9,791.1,156.8,791,151.7 M798.7,177.5
|
||||
c8.8,0,16-1.9,22.6-6.3v5h24.8v-52.5c0-20-13.5-30.9-35.9-30.9c-12.6,0-25,2.9-38.3,9l9,18.4c9.6-4,17.7-6.5,24.8-6.5
|
||||
c10.3,0,15.6,4,15.6,12.2v4c-6.1-1.6-12.3-2.4-18.6-2.3c-21.1,0-33.8,8.8-33.8,24.6C768.9,166.6,780.4,177.6,798.7,177.5
|
||||
M662.5,176.2h26.7v-42.5h44.6v42.5h26.7V67.7h-26.6v41.7h-44.6V67.7h-26.7L662.5,176.2z M561,135.1c0-11.8,9.3-20.8,21.5-20.8
|
||||
c6.4-0.1,12.6,2.1,17.4,6.4v28.6c-4.7,4.4-10.9,6.7-17.4,6.5C570.5,155.8,561,146.8,561,135.1 M600.2,176.1H625V62.3l-25,5.4v30.8
|
||||
c-6.4-3.6-13.6-5.5-20.9-5.4c-23.9,0-42.6,18.4-42.6,42c-0.3,23,18.1,41.9,41.1,42.2c0.2,0,0.5,0,0.7,0c7.9,0,15.6-2.5,22-7.1V176.1
|
||||
z M486.5,113.2c7.9,0,14.6,5.1,17.2,13h-34.2C471.9,118,478.2,113.2,486.5,113.2 M444.2,135.2c0,23.9,19.5,42.5,44.6,42.5
|
||||
c13.8,0,23.9-3.7,34.3-12.4l-16.6-14.7c-3.9,4-9.6,6.2-16.4,6.2c-8.8,0.2-16.8-4.9-20.2-13h58.4v-6.2c0-26-17.5-44.8-41.4-44.8
|
||||
c-23.2-0.4-42.4,18.2-42.7,41.5C444.2,134.6,444.2,134.9,444.2,135.2 M400.9,90.5c8.8,0,13.8,5.6,13.8,12.2s-5,12.2-13.8,12.2h-26.3
|
||||
V90.5H400.9z M347.9,176.2h26.7v-39.5h20.3l20.5,39.5h29.7l-23.9-43.4c12.4-5,20.5-17.1,20.4-30.5c0-19.5-15.3-34.5-38.3-34.5H348
|
||||
L347.9,176.2z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 45 KiB |
@@ -0,0 +1,24 @@
|
||||
- name: create HTML report
|
||||
ansible.builtin.template:
|
||||
src: report.j2
|
||||
dest: "{{ file_path }}/network.html"
|
||||
|
||||
- name: copy CSS over
|
||||
ansible.builtin.copy:
|
||||
src: "css"
|
||||
dest: "{{ file_path }}"
|
||||
directory_mode: true
|
||||
|
||||
- name: copy logos over
|
||||
ansible.builtin.copy:
|
||||
src: "{{ item }}"
|
||||
dest: "{{ file_path }}"
|
||||
directory_mode: true
|
||||
loop:
|
||||
- "webpage_logo.png"
|
||||
- "redhat-ansible-logo.svg"
|
||||
- "router.png"
|
||||
|
||||
- name: display link to inventory report
|
||||
ansible.builtin.debug:
|
||||
msg: "Please go to http://{{ ansible_host }}/network.html"
|
||||
@@ -0,0 +1,31 @@
|
||||
<!–– INTERNAL TABLE FOR BGP --!>
|
||||
<div id="accordion">
|
||||
<div class="ui-accordion ui-widget ui-helper-reset" role="tablist">
|
||||
<h3 class="ui-accordion-header ui-corner-top ui-state-default ui-accordion-icons ui-accordion-header-collapsed ui-corner-all" role="tab" id="ui-id-3" aria-controls="ui-id-4" aria-selected="false" aria-expanded="false" tabindex="0">BGP Global Info</h3>
|
||||
<div class="net_content ui-accordion-content ui-corner-bottom ui-helper-reset ui-widget-content" id="ui-id-4" aria-labelledby="ui-id-3" role="tabpanel" aria-hidden="true" style="display: none; height: 194px;">
|
||||
{% if hostvars[network_switch]['ansible_network_resources']['bgp_global'] is defined and hostvars[network_switch]['ansible_network_resources']['bgp_global']|length > 0 %}
|
||||
<table id="subtable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ASN</th>
|
||||
<th>Router ID</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for bgpinfo in hostvars[network_switch]['ansible_network_resources']['bgp_global'] %}
|
||||
<tr>
|
||||
<td>{{bgpinfo['as_number']}}</td>
|
||||
<td>{{bgpinfo['router_id']|default("Not Configured")}}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% elif hostvars[network_switch]['ansible_network_resources']['bgp_global'] is defined and hostvars[network_switch]['ansible_network_resources']['bgp_global']|length == 0 %}
|
||||
BGP is not configured on this device
|
||||
{% else %}
|
||||
No BGP information available
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!–– END INTERNAL TABLE FOR BGP --!>
|
||||
@@ -0,0 +1,15 @@
|
||||
|
||||
|
||||
<div class="wrapper">
|
||||
<header>
|
||||
<div class="header-container">
|
||||
<a href="https://ansible.com">
|
||||
<img
|
||||
class="header-logo"
|
||||
src="redhat-ansible-logo.svg"
|
||||
title="Red Hat Ansible"
|
||||
alt="Red Hat Ansible"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
</header>
|
||||
@@ -0,0 +1,41 @@
|
||||
<!–– INTERNAL TABLE FOR INTERFACES --!>
|
||||
<div id="accordion">
|
||||
<div class="ui-accordion ui-widget ui-helper-reset" role="tablist">
|
||||
<h3 class="ui-accordion-header ui-corner-top ui-state-default ui-accordion-icons ui-accordion-header-collapsed ui-corner-all" role="tab" id="ui-id-3" aria-controls="ui-id-4" aria-selected="false" aria-expanded="false" tabindex="0">
|
||||
Interfaces - MTU/Duplex/Speed
|
||||
</h3>
|
||||
<div class="net_content ui-accordion-content ui-corner-bottom ui-helper-reset ui-widget-content" id="ui-id-4" aria-labelledby="ui-id-3" role="tabpanel" aria-hidden="true" style="display: none; height: 194px;">
|
||||
{% if hostvars[network_switch]['ansible_network_resources']['interfaces'] is defined and hostvars[network_switch]['ansible_network_resources']['interfaces']|length > 0 %}
|
||||
<table id="subtable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Interface Name</th>
|
||||
<th>Description</th>
|
||||
<th>Duplex</th>
|
||||
<th>Enabled</th>
|
||||
<th>MTU</th>
|
||||
<th>Speed</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for interface in hostvars[network_switch]['ansible_network_resources']['interfaces'] %}
|
||||
<tr>
|
||||
<td>{{interface['name']}}</td>
|
||||
<td>{{interface['description']|default("none")}}</td>
|
||||
<td>{{interface['duplex']|default("default")}}</td>
|
||||
<td>{{interface['enabled']}}</td>
|
||||
<td>{{interface['mtu']|default("default")}}</td>
|
||||
<td>{{interface['speed']|default("default")}}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% elif hostvars[network_switch]['ansible_network_resources']['interfaces'] is defined and hostvars[network_switch]['ansible_network_resources']['interfaces']|length == 0 %}
|
||||
No interfaces configured on this device
|
||||
{% else %}
|
||||
No Interface information available
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!–– END INTERNAL TABLE FOR INTERFACES --!>
|
||||
@@ -0,0 +1,37 @@
|
||||
<!–– INTERNAL TABLE FOR l2_interfaces --!>
|
||||
<div id="accordion">
|
||||
<div class="ui-accordion ui-widget ui-helper-reset" role="tablist">
|
||||
<h3 class="ui-accordion-header ui-corner-top ui-state-default ui-accordion-icons ui-accordion-header-collapsed ui-corner-all" role="tab" id="ui-id-3" aria-controls="ui-id-4" aria-selected="false" aria-expanded="false" tabindex="0">L2 Interfaces - Trunk/Access Ports</h3>
|
||||
<div class="net_content ui-accordion-content ui-corner-bottom ui-helper-reset ui-widget-content" id="ui-id-4" aria-labelledby="ui-id-3" role="tabpanel" aria-hidden="true" style="display: none; height: 194px;">
|
||||
{% if hostvars[network_switch]['ansible_network_resources']['l2_interfaces'] is defined and hostvars[network_switch]['ansible_network_resources']['l2_interfaces']|length > 0 %}
|
||||
<table id="subtable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Mode</th>
|
||||
<th>Access VLAN</th>
|
||||
<th>Trunk Native VLAN</th>
|
||||
<th>Trunk Allowed VLANs</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for l2_interface in hostvars[network_switch]['ansible_network_resources']['l2_interfaces'] %}
|
||||
<tr>
|
||||
<td>{{l2_interface['name']}}</td>
|
||||
<td>{{l2_interface['mode']|default("Not Configured")}}</td>
|
||||
<td>{{l2_interface['access']['vlan']|default("Not Configured")}}</td>
|
||||
<td>{{l2_interface['trunk']['native_vlan']|default("Not Configured")}}</td>
|
||||
<td>{{l2_interface['trunk']['trunk_allowed_vlans']|default("Not Configured")}}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% elif hostvars[network_switch]['ansible_network_resources']['l2_interfaces'] is defined and hostvars[network_switch]['ansible_network_resources']['l2_interfaces']|length == 0 %}
|
||||
L2 information is not configured on this device
|
||||
{% else %}
|
||||
No L2 information available
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!–– END INTERNAL TABLE FOR l2_interfaces --!>
|
||||
@@ -0,0 +1,58 @@
|
||||
<!–– INTERNAL TABLE FOR L3_INTERFACES --!>
|
||||
<div id="accordion">
|
||||
<div class="ui-accordion ui-widget ui-helper-reset" role="tablist">
|
||||
<h3 class="ui-accordion-header ui-corner-top ui-state-default ui-accordion-icons ui-accordion-header-collapsed ui-corner-all" role="tab" id="ui-id-3" aria-controls="ui-id-4" aria-selected="false" aria-expanded="false" tabindex="0">L3 Interfaces - IP Addresses</h3>
|
||||
<div class="net_content ui-accordion-content ui-corner-bottom ui-helper-reset ui-widget-content" id="ui-id-4" aria-labelledby="ui-id-3" role="tabpanel" aria-hidden="true" style="display: none; height: 194px;">
|
||||
{% if hostvars[network_switch]['ansible_network_resources']['l3_interfaces'] is defined and hostvars[network_switch]['ansible_network_resources']['l3_interfaces']|length > 0 %}
|
||||
<table id="subtable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Interface Name</th>
|
||||
<th>IPv4</th>
|
||||
<th>IPv6</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for interface in hostvars[network_switch]['ansible_network_resources']['l3_interfaces'] %}
|
||||
<tr>
|
||||
<td>{{interface['name']}}</td>
|
||||
<!–– INTERNAL IPv4 LOOP FOR L3_INTERFACES --!>
|
||||
<td>
|
||||
{% if interface.ipv4 is defined %}
|
||||
{% for address in interface.ipv4 %}
|
||||
{% if address['address'] is defined %}
|
||||
{{address['address']}}
|
||||
{% else %}
|
||||
dhcp
|
||||
{% endif %}
|
||||
{% if address['secondary'] is defined %}
|
||||
secondary
|
||||
{% endif %}
|
||||
{% if loop.length > 1 and not loop.last %}<br>{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</td>
|
||||
<!–– END IPv4 INTERNAL LOOP FOR L3_INTERFACES --!>
|
||||
<!–– INTERNAL IPv6 LOOP FOR L3_INTERFACES --!>
|
||||
<td>
|
||||
{% if interface.ipv6 is defined %}
|
||||
{% for v6address in interface.ipv6 %}
|
||||
{{v6address['address']}}
|
||||
{% if loop.length > 1 and not loop.last %}<br>{% endif %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</td>
|
||||
<!–– END INTERNAL LOOP FOR L3_INTERFACES --!>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% elif hostvars[network_switch]['ansible_network_resources']['l3_interfaces'] is defined and hostvars[network_switch]['ansible_network_resources']['l3_interfaces']|length == 0 %}
|
||||
L3 information is not configured on this device
|
||||
{% else %}
|
||||
No L3 information available
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!–– END INTERNAL TABLE FOR L3_INTERFACES --!>
|
||||
@@ -0,0 +1,29 @@
|
||||
<!–– INTERNAL TABLE FOR LACP --!>
|
||||
<div id="accordion">
|
||||
<div class="ui-accordion ui-widget ui-helper-reset" role="tablist">
|
||||
<h3 class="ui-accordion-header ui-corner-top ui-state-default ui-accordion-icons ui-accordion-header-collapsed ui-corner-all" role="tab" id="ui-id-3" aria-controls="ui-id-4" aria-selected="false" aria-expanded="false" tabindex="0">LACP</h3>
|
||||
<div class="net_content ui-accordion-content ui-corner-bottom ui-helper-reset ui-widget-content" id="ui-id-4" aria-labelledby="ui-id-3" role="tabpanel" aria-hidden="true" style="display: none; height: 194px;">
|
||||
{% if hostvars[network_switch]['ansible_network_resources']['lacp'] is defined and hostvars[network_switch]['ansible_network_resources']['lacp'].keys()|length > 0 %}
|
||||
<table id="subtable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>System Priority</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
{% if hostvars[network_switch]['ansible_network_resources']['lacp']['system'] is defined %}
|
||||
<td> {{hostvars[network_switch]['ansible_network_resources']['lacp']['system']['priority']}}</td>
|
||||
{% endif %}
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
{% elif hostvars[network_switch]['ansible_network_resources']['lacp'] is defined and hostvars[network_switch]['ansible_network_resources']['lacp']|length == 0 %}
|
||||
LACP is not configured on this device
|
||||
{% else %}
|
||||
No LACP information available
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!–– END INTERNAL TABLE FOR LACP --!>
|
||||
@@ -0,0 +1,33 @@
|
||||
<!–– INTERNAL TABLE FOR lldp_interfaces --!>
|
||||
<div id="accordion">
|
||||
<div class="ui-accordion ui-widget ui-helper-reset" role="tablist">
|
||||
<h3 class="ui-accordion-header ui-corner-top ui-state-default ui-accordion-icons ui-accordion-header-collapsed ui-corner-all" role="tab" id="ui-id-3" aria-controls="ui-id-4" aria-selected="false" aria-expanded="false" tabindex="0">LLDP Interfaces</h3>
|
||||
<div class="net_content ui-accordion-content ui-corner-bottom ui-helper-reset ui-widget-content" id="ui-id-4" aria-labelledby="ui-id-3" role="tabpanel" aria-hidden="true" style="display: none; height: 194px;">
|
||||
{% if hostvars[network_switch]['ansible_network_resources']['lldp_interfaces'] is defined and hostvars[network_switch]['ansible_network_resources']['lldp_interfaces']|length > 0 %}
|
||||
<table id="subtable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>vlan_id</th>
|
||||
<th>Name</th>
|
||||
<th>state</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for interface in hostvars[network_switch]['ansible_network_resources']['lldp_interfaces'] %}
|
||||
<tr>
|
||||
<td>{{interface['name']}}</td>
|
||||
<td>{{interface['receive']|default("Not Configured")}}</td>
|
||||
<td>{{interface['transmit']|default("Not Configured")}}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% elif hostvars[network_switch]['ansible_network_resources']['lldp_interfaces'] is defined and hostvars[network_switch]['ansible_network_resources']['lldp_interfaces'].keys()|length == 0 %}
|
||||
LLDP is not configured on this device
|
||||
{% else %}
|
||||
No LLDP information available
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!–– END INTERNAL TABLE FOR lldp_interfaces --!>
|
||||
@@ -0,0 +1,29 @@
|
||||
<!–– INTERNAL TABLE FOR OSPF --!>
|
||||
<div id="accordion">
|
||||
<div class="ui-accordion ui-widget ui-helper-reset" role="tablist">
|
||||
<h3 class="ui-accordion-header ui-corner-top ui-state-default ui-accordion-icons ui-accordion-header-collapsed ui-corner-all" role="tab" id="ui-id-3" aria-controls="ui-id-4" aria-selected="false" aria-expanded="false" tabindex="0">OSPF Global Info</h3>
|
||||
<div class="net_content ui-accordion-content ui-corner-bottom ui-helper-reset ui-widget-content" id="ui-id-4" aria-labelledby="ui-id-3" role="tabpanel" aria-hidden="true" style="display: none; height: 194px;">
|
||||
{% if hostvars[network_switch]['ansible_network_resources']['ospfv2'] is defined and hostvars[network_switch]['ansible_network_resources']['ospfv2']|length > 0 %}
|
||||
<table id="subtable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Process ID</th>
|
||||
<th>Router ID</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>1</td>
|
||||
<td>{{ hostvars[network_switch]['ansible_network_resources']['ospfv2']['parameters']['router_id'] }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
{% elif hostvars[network_switch]['ansible_network_resources']['ospfv2'] is defined and hostvars[network_switch]['ansible_network_resources']['ospfv2']|length == 0 %}
|
||||
OSPF is not configured on this device
|
||||
{% else %}
|
||||
No OSPF information available
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!–– END INTERNAL TABLE FOR OSPF --!>
|
||||
@@ -0,0 +1,111 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title> Ansible Network Automation Report </title>
|
||||
<link rel="stylesheet" type="text/css" href="//fonts.googleapis.com/css?family=Open+Sans" />
|
||||
<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
|
||||
<link rel="stylesheet" href="css/new.css">
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
|
||||
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
|
||||
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
|
||||
<script>
|
||||
$(function() {
|
||||
$( "#accordion > div" ).accordion({
|
||||
header: "h3",
|
||||
active: false,
|
||||
collapsible: true
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<script>
|
||||
(function(document) {
|
||||
'use strict';
|
||||
|
||||
var TableFilter = (function(myArray) {
|
||||
var search_input;
|
||||
|
||||
function _onInputSearch(e) {
|
||||
search_input = e.target;
|
||||
var tables = document.getElementsByClassName(search_input.getAttribute('data-table'));
|
||||
myArray.forEach.call(tables, function(table) {
|
||||
myArray.forEach.call(table.tBodies, function(tbody) {
|
||||
myArray.forEach.call(tbody.rows, function(row) {
|
||||
var text_content = row.textContent.toLowerCase();
|
||||
var search_val = search_input.value.toLowerCase();
|
||||
row.style.display = text_content.indexOf(search_val) > -1 ? '' : 'none';
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
init: function() {
|
||||
var inputs = document.getElementsByClassName('search-input');
|
||||
myArray.forEach.call(inputs, function(input) {
|
||||
input.oninput = _onInputSearch;
|
||||
});
|
||||
}
|
||||
};
|
||||
})(Array.prototype);
|
||||
|
||||
document.addEventListener('readystatechange', function() {
|
||||
if (document.readyState === 'complete') {
|
||||
TableFilter.init();
|
||||
}
|
||||
});
|
||||
|
||||
})(document);
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="wrapper">
|
||||
{% include 'header.j2' %}
|
||||
<section>
|
||||
<center>
|
||||
<h1>Ansible Network Automation Report</h1>
|
||||
<h3><input type="search" placeholder="Search..." class="form-control search-input" data-table="main_net_table"/>
|
||||
</center>
|
||||
<table class="table table-striped mt32 main_net_table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Network Device</th>
|
||||
<th>Layer 1</th>
|
||||
<th>Layer 2</th>
|
||||
<th>Layer 3</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for network_switch in groups['tag_Router']|sort %}
|
||||
<tr>
|
||||
<td class="summary_info">
|
||||
<div id="hostname">
|
||||
<p class="hostname">
|
||||
<img class="router_image" src="router.png"> {{ hostvars[network_switch]['ansible_net_hostname'].split('.')[0] }}</p>
|
||||
</div>
|
||||
{% include 'summary.j2' %}
|
||||
</td>
|
||||
<td>
|
||||
{% include 'interfaces.j2' %}
|
||||
</td>
|
||||
<td>
|
||||
{% include 'vlans.j2' %}
|
||||
{% include 'lldp_interfaces.j2' %}
|
||||
{% include 'l2_interfaces.j2' %}
|
||||
</td>
|
||||
<td>
|
||||
{% include 'l3_interfaces.j2' %}
|
||||
{% include 'lacp.j2' %}
|
||||
{% include 'bgp.j2' %}
|
||||
{% include 'ospf.j2' %}
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<center><p>Created with</p><br><img src="webpage_logo.png" width="300">
|
||||
</center>
|
||||
</section>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,25 @@
|
||||
<div id="net_info_div">
|
||||
<table class="net_info">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Platform</td>
|
||||
<td class="sub_net_info">{{hostvars[network_switch]['ansible_net_system']}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Code Version</td>
|
||||
<td class="sub_net_info">{{hostvars[network_switch]['ansible_net_version']}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Model</td>
|
||||
<td class="sub_net_info">{{hostvars[network_switch]['ansible_net_model']|default("N/A")}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Serial Number</td>
|
||||
<td class="sub_net_info">{{hostvars[network_switch]['ansible_net_serialnum']}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Transport</td>
|
||||
<td class="sub_net_info">{{hostvars[network_switch]['ansible_net_api']}}</td>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@@ -0,0 +1,33 @@
|
||||
<!–– INTERNAL TABLE FOR VLANS --!>
|
||||
<div id="accordion">
|
||||
<div class="ui-accordion ui-widget ui-helper-reset" role="tablist">
|
||||
<h3 class="ui-accordion-header ui-corner-top ui-state-default ui-accordion-icons ui-accordion-header-collapsed ui-corner-all" role="tab" id="ui-id-3" aria-controls="ui-id-4" aria-selected="false" aria-expanded="false" tabindex="0">VLANs</h3>
|
||||
<div class="net_content ui-accordion-content ui-corner-bottom ui-helper-reset ui-widget-content" id="ui-id-4" aria-labelledby="ui-id-3" role="tabpanel" aria-hidden="true" style="display: none; height: 194px;">
|
||||
{% if hostvars[network_switch]['ansible_network_resources']['vlans'] is defined and hostvars[network_switch]['ansible_network_resources']['vlans']|length > 0 %}
|
||||
<table id="subtable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>vlan_id</th>
|
||||
<th>Name</th>
|
||||
<th>state</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for vlan in hostvars[network_switch]['ansible_network_resources']['vlans'] %}
|
||||
<tr>
|
||||
<td>{{vlan['vlan_id']}}</td>
|
||||
<td>{{vlan['name']|default("none")}}</td>
|
||||
<td>{{vlan['state']|default("default")}}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% elif hostvars[network_switch]['ansible_network_resources']['vlans'] is defined and hostvars[network_switch]['ansible_network_resources']['vlans']|length == 0 %}
|
||||
VLANs are not configured on this device
|
||||
{% else %}
|
||||
No VLAN information available
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!–– END INTERNAL TABLE FOR VLANS --!>
|
||||
@@ -0,0 +1 @@
|
||||
file_path: /var/www/html
|
||||
@@ -0,0 +1,36 @@
|
||||
build_report_windows
|
||||
========
|
||||
|
||||
Installs Apache and creates a report based on facts from Windows services and packages modules
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
Must run on Apache server
|
||||
|
||||
Role Variables / Configuration
|
||||
--------------
|
||||
|
||||
N/A
|
||||
|
||||
Dependencies
|
||||
------------
|
||||
|
||||
N/A
|
||||
|
||||
Example Playbook
|
||||
----------------
|
||||
|
||||
The role can be used to create an html report on any number of Linux hosts using any number of Windows servers about their services and packages installed
|
||||
|
||||
|
||||
```
|
||||
---
|
||||
- hosts: all
|
||||
|
||||
tasks:
|
||||
- name: Run Windows Report
|
||||
import_role:
|
||||
name: shadowman.reports.build_report_windows
|
||||
|
||||
```
|
||||
@@ -0,0 +1,2 @@
|
||||
---
|
||||
detailedreport: True
|
||||
|
After Width: | Height: | Size: 52 KiB |
@@ -0,0 +1,202 @@
|
||||
p.hostname {
|
||||
color: #000000;
|
||||
font-weight: bolder;
|
||||
font-size: large;
|
||||
margin: auto;
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
#subtable {
|
||||
background: #ebebeb;
|
||||
margin: 0px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#subtable tbody tr td {
|
||||
padding: 5px 5px 5px 5px;
|
||||
}
|
||||
|
||||
#subtable thead th {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
* {
|
||||
-moz-box-sizing: border-box;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
font-family: "Open Sans", "Helvetica";
|
||||
|
||||
}
|
||||
|
||||
a {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
p {
|
||||
color: #ffffff;
|
||||
}
|
||||
h1 {
|
||||
text-align: center;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
body {
|
||||
background:#353a40;
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: separate;
|
||||
background:#fff;
|
||||
@include border-radius(5px);
|
||||
@include box-shadow(0px 0px 5px rgba(0,0,0,0.3));
|
||||
}
|
||||
|
||||
.main_net_table {
|
||||
margin:50px auto;
|
||||
}
|
||||
|
||||
thead {
|
||||
@include border-radius(5px);
|
||||
}
|
||||
|
||||
thead th {
|
||||
font-size:16px;
|
||||
font-weight:400;
|
||||
color:#fff;
|
||||
@include text-shadow(1px 1px 0px rgba(0,0,0,0.5));
|
||||
text-align:left;
|
||||
padding:20px;
|
||||
border-top:1px solid #858d99;
|
||||
background: #353a40;
|
||||
|
||||
&:first-child {
|
||||
@include border-top-left-radius(5px);
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
@include border-top-right-radius(5px);
|
||||
}
|
||||
}
|
||||
|
||||
tbody tr td {
|
||||
font-weight:400;
|
||||
color:#5f6062;
|
||||
font-size:13px;
|
||||
padding:20px 20px 20px 20px;
|
||||
border-bottom:1px solid #e0e0e0;
|
||||
}
|
||||
|
||||
tbody tr:nth-child(2n) {
|
||||
background:#f0f3f5;
|
||||
}
|
||||
|
||||
tbody tr:last-child td {
|
||||
border-bottom:none;
|
||||
&:first-child {
|
||||
@include border-bottom-left-radius(5px);
|
||||
}
|
||||
&:last-child {
|
||||
@include border-bottom-right-radius(5px);
|
||||
}
|
||||
}
|
||||
|
||||
td {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
span.highlight {
|
||||
background-color: yellow;
|
||||
}
|
||||
|
||||
.expandclass {
|
||||
color: #5f6062;
|
||||
}
|
||||
|
||||
.content{
|
||||
display:none;
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
header {
|
||||
width: 100%;
|
||||
position: initial;
|
||||
float: initial;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
border-radius: 0;
|
||||
height: 88px;
|
||||
background-color: #171717;
|
||||
}
|
||||
|
||||
.header-container {
|
||||
margin: 0 auto;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
max-width: 1170px;
|
||||
padding: 0;
|
||||
float: initial;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.header-logo {
|
||||
width: 137px;
|
||||
border: 0;
|
||||
margin: 0;
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
.header-link {
|
||||
margin-left: 40px;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
text-transform: uppercase;
|
||||
font-size: 15px;
|
||||
font-family: 'Red Hat Text';
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.header-link:hover {
|
||||
text-shadow: 0 0 0.02px white;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
table.net_info td {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
p.expandclass:hover {
|
||||
text-decoration: underline;
|
||||
color: #EE0000;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.summary_info {
|
||||
}
|
||||
|
||||
.ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active, a.ui-button:active, .ui-button:active, .ui-button.ui-state-active:hover {
|
||||
border: 1px solid #5F0000;
|
||||
background: #EE0000;
|
||||
}
|
||||
|
||||
div#net_content {
|
||||
padding: 0px;
|
||||
height: auto !important;
|
||||
}
|
||||
|
||||
img.router_image {
|
||||
vertical-align: middle;
|
||||
padding: 0px 10px 10px 10px;
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
table.net_info {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
p.internal_label {
|
||||
color: #000000;
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 24.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Logos" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="930.2px" height="350px" viewBox="0 0 930.2 350" style="enable-background:new 0 0 930.2 350;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFFFFF;}
|
||||
.st1{fill:#EE0000;}
|
||||
</style>
|
||||
<title>Logo-Red_Hat-Ansible_Automation_Platform-A-Reverse-RGB</title>
|
||||
<path class="st0" d="M383.3,228.5h18.8L446,335.7h-17.5l-12.4-31.4h-48l-12.6,31.4h-16.7L383.3,228.5z M410.9,291l-18.7-47l-18.7,47
|
||||
H410.9z"/>
|
||||
<path class="st0" d="M455.2,257.7h15.3v7.8c6.2-6.2,14.7-9.6,23.5-9.3c17.9,0,30.5,12.4,30.5,30.5v49h-15.3v-46.5
|
||||
c0-12.3-7.5-19.8-19.3-19.8c-7.8-0.3-15.1,3.6-19.3,10.1v56.1h-15.3V257.7z"/>
|
||||
<path class="st0" d="M543,315.5c8.1,6.4,16.7,9.8,25.4,9.8c11,0,18.7-4.8,18.7-11.7c0-5.5-4-8.7-12.6-10l-14.1-2
|
||||
c-15.5-2.3-23.3-9.5-23.3-21.6c0-14.1,12.3-23.6,30.5-23.6c11.3-0.1,22.3,3.4,31.5,9.9l-7.8,10.1c-8.6-5.7-16.4-8.1-24.7-8.1
|
||||
c-9.3,0-15.6,4.3-15.6,10.6c0,5.7,3.7,8.4,12.9,9.8l14.1,2c15.5,2.3,23.6,9.7,23.6,21.7c0,14-14.1,24.5-32.6,24.5
|
||||
c-13.5,0-25.6-4-34.2-11.5L543,315.5z"/>
|
||||
<path class="st0" d="M611.6,235.6c0-5.2,4.1-9.4,9.3-9.5c0,0,0,0,0,0c5.2-0.2,9.7,3.9,9.9,9.1c0.2,5.2-3.9,9.7-9.1,9.9
|
||||
c-0.2,0-0.5,0-0.7,0C615.8,245.1,611.6,240.9,611.6,235.6C611.6,235.7,611.6,235.7,611.6,235.6z M628.6,335.7h-15.3v-78h15.3V335.7z
|
||||
"/>
|
||||
<path class="st0" d="M685.5,336.9c-8.5,0-16.8-2.7-23.6-7.8v6.6h-15.2V228.5l15.3-3.4v40c6.6-5.6,15.1-8.7,23.7-8.6
|
||||
c22.1,0,39.4,17.7,39.4,40.1C725.2,319.1,707.9,336.9,685.5,336.9z M662,279.2v35.2c4.9,5.7,13,9.2,21.8,9.2
|
||||
c15,0,26.4-11.5,26.4-26.8c0-15.3-11.5-27-26.4-27C674.9,269.8,667.1,273.2,662,279.2z"/>
|
||||
<path class="st0" d="M755,335.7h-15.3V228.5l15.3-3.4V335.7z"/>
|
||||
<path class="st0" d="M810.5,337.1c-23,0-40.9-17.7-40.9-40.4c0-22.5,17.2-40.1,39.1-40.1c21.5,0,37.7,17.8,37.7,40.8v4.4h-61.6
|
||||
c2,13,13.2,22.5,26.4,22.4c7.2,0.2,14.2-2.3,19.8-6.8l9.8,9.7C832.1,333.7,821.5,337.4,810.5,337.1z M784.9,290.2h46.3
|
||||
c-2.3-11.9-11.5-20.8-22.8-20.8C796.5,269.4,787.2,277.8,784.9,290.2z"/>
|
||||
<path class="st1" d="M202.8,137.5c18.4,0,45.1-3.8,45.1-25.7c0.1-1.7-0.1-3.4-0.5-5l-11-47.7c-2.5-10.5-4.8-15.2-23.2-24.5
|
||||
c-14.3-7.3-45.5-19.4-54.7-19.4c-8.6,0-11.1,11.1-21.3,11.1c-9.8,0-17.1-8.3-26.4-8.3c-8.8,0-14.6,6-19,18.4c0,0-12.4,34.9-14,40
|
||||
c-0.3,0.9-0.4,1.9-0.4,2.9C77.6,92.9,131.1,137.5,202.8,137.5 M250.8,120.7c2.5,12.1,2.5,13.3,2.5,14.9c0,20.6-23.2,32.1-53.7,32.1
|
||||
c-69,0-129.3-40.3-129.3-67c0-3.7,0.8-7.4,2.2-10.8c-24.8,1.3-56.9,5.7-56.9,34c0,46.4,109.9,103.5,196.9,103.5
|
||||
c66.7,0,83.5-30.2,83.5-54C296.1,154.6,279.9,133.4,250.8,120.7"/>
|
||||
<path d="M250.7,120.7c2.5,12.1,2.5,13.3,2.5,14.9c0,20.6-23.2,32.1-53.7,32.1c-69,0-129.3-40.3-129.3-67c0-3.7,0.8-7.4,2.2-10.8
|
||||
l5.4-13.3c-0.3,0.9-0.4,1.9-0.4,2.8c0,13.6,53.5,58.1,125.2,58.1c18.4,0,45.1-3.8,45.1-25.7c0.1-1.7-0.1-3.4-0.5-5L250.7,120.7z"/>
|
||||
<path class="st0" d="M869.1,151.2c0,17.5,10.5,26,29.7,26c5.9-0.1,11.8-1,17.5-2.5v-20.3c-3.7,1.2-7.5,1.7-11.3,1.7
|
||||
c-7.9,0-10.8-2.5-10.8-9.9v-31.1h22.9V94.2h-22.9V67.7l-25,5.4v21.1h-16.6v20.9h16.6L869.1,151.2z M791,151.7
|
||||
c0-5.4,5.4-8.1,13.6-8.1c5,0,10,0.7,14.9,1.9V156c-4.8,2.6-10.2,3.9-15.6,3.9C795.9,159.9,791.1,156.8,791,151.7 M798.7,177.5
|
||||
c8.8,0,16-1.9,22.6-6.3v5h24.8v-52.5c0-20-13.5-30.9-35.9-30.9c-12.6,0-25,2.9-38.3,9l9,18.4c9.6-4,17.7-6.5,24.8-6.5
|
||||
c10.3,0,15.6,4,15.6,12.2v4c-6.1-1.6-12.3-2.4-18.6-2.3c-21.1,0-33.8,8.8-33.8,24.6C768.9,166.6,780.4,177.6,798.7,177.5
|
||||
M662.5,176.2h26.7v-42.5h44.6v42.5h26.7V67.7h-26.6v41.7h-44.6V67.7h-26.7L662.5,176.2z M561,135.1c0-11.8,9.3-20.8,21.5-20.8
|
||||
c6.4-0.1,12.6,2.1,17.4,6.4v28.6c-4.7,4.4-10.9,6.7-17.4,6.5C570.5,155.8,561,146.8,561,135.1 M600.2,176.1H625V62.3l-25,5.4v30.8
|
||||
c-6.4-3.6-13.6-5.5-20.9-5.4c-23.9,0-42.6,18.4-42.6,42c-0.3,23,18.1,41.9,41.1,42.2c0.2,0,0.5,0,0.7,0c7.9,0,15.6-2.5,22-7.1V176.1
|
||||
z M486.5,113.2c7.9,0,14.6,5.1,17.2,13h-34.2C471.9,118,478.2,113.2,486.5,113.2 M444.2,135.2c0,23.9,19.5,42.5,44.6,42.5
|
||||
c13.8,0,23.9-3.7,34.3-12.4l-16.6-14.7c-3.9,4-9.6,6.2-16.4,6.2c-8.8,0.2-16.8-4.9-20.2-13h58.4v-6.2c0-26-17.5-44.8-41.4-44.8
|
||||
c-23.2-0.4-42.4,18.2-42.7,41.5C444.2,134.6,444.2,134.9,444.2,135.2 M400.9,90.5c8.8,0,13.8,5.6,13.8,12.2s-5,12.2-13.8,12.2h-26.3
|
||||
V90.5H400.9z M347.9,176.2h26.7v-39.5h20.3l20.5,39.5h29.7l-23.9-43.4c12.4-5,20.5-17.1,20.4-30.5c0-19.5-15.3-34.5-38.3-34.5H348
|
||||
L347.9,176.2z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 45 KiB |
@@ -0,0 +1,24 @@
|
||||
- name: create HTML report
|
||||
ansible.builtin.template:
|
||||
src: report.j2
|
||||
dest: "{{ file_path }}/windows.html"
|
||||
|
||||
- name: copy CSS over
|
||||
ansible.builtin.copy:
|
||||
src: "css"
|
||||
dest: "{{ file_path }}"
|
||||
directory_mode: true
|
||||
|
||||
- name: copy logos over
|
||||
ansible.builtin.copy:
|
||||
src: "{{ item }}"
|
||||
dest: "{{ file_path }}"
|
||||
directory_mode: true
|
||||
loop:
|
||||
- "webpage_logo.png"
|
||||
- "redhat-ansible-logo.svg"
|
||||
- "server.png"
|
||||
|
||||
- name: display link to inventory report
|
||||
ansible.builtin.debug:
|
||||
msg: "Please go to http://{{ ansible_host }}/windows.html"
|
||||
@@ -0,0 +1,15 @@
|
||||
|
||||
|
||||
<div class="wrapper">
|
||||
<header>
|
||||
<div class="header-container">
|
||||
<a href="https://ansible.com">
|
||||
<img
|
||||
class="header-logo"
|
||||
src="redhat-ansible-logo.svg"
|
||||
title="Red Hat Ansible"
|
||||
alt="Red Hat Ansible"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
</header>
|
||||
@@ -0,0 +1,29 @@
|
||||
<!–– INTERNAL TABLE FOR PACKAGES --!>
|
||||
<div id="accordion">
|
||||
<div class="ui-accordion ui-widget ui-helper-reset" role="tablist">
|
||||
<h3 class="ui-accordion-header ui-corner-top ui-state-default ui-accordion-icons ui-accordion-header-collapsed ui-corner-all" role="tab" id="ui-id-3" aria-controls="ui-id-4" aria-selected="false" aria-expanded="false" tabindex="0">Package Facts</h3>
|
||||
<div class="net_content ui-accordion-content ui-corner-bottom ui-helper-reset ui-widget-content" id="ui-id-4" aria-labelledby="ui-id-3" role="tabpanel" aria-hidden="true" style="display: none; height: 194px;">
|
||||
<table id="subtable" class="sortable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Package Name</th>
|
||||
<th>Version</th>
|
||||
<th>Publisher</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% if hostvars[windows_host]['packages'] is defined %}
|
||||
{% for package in hostvars[windows_host]['packages'] %}
|
||||
<tr>
|
||||
<td>{{package['name']}}</td>
|
||||
<td>{{package['version']}}</td>
|
||||
<td>{{package['publisher']}}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!–– END INTERNAL TABLE FOR PACKAGES --!>
|
||||
@@ -0,0 +1,101 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title> Ansible Windows Automation Report </title>
|
||||
<link rel="stylesheet" type="text/css" href="//fonts.googleapis.com/css?family=Open+Sans" />
|
||||
<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
|
||||
<link rel="stylesheet" href="css/new.css">
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
|
||||
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
|
||||
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
|
||||
<script src="https://www.kryogenix.org/code/browser/sorttable/sorttable.js"></script>
|
||||
<script>
|
||||
$(function() {
|
||||
$( "#accordion > div" ).accordion({
|
||||
header: "h3",
|
||||
active: false,
|
||||
collapsible: true
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<script>
|
||||
(function(document) {
|
||||
'use strict';
|
||||
|
||||
var TableFilter = (function(myArray) {
|
||||
var search_input;
|
||||
|
||||
function _onInputSearch(e) {
|
||||
search_input = e.target;
|
||||
var tables = document.getElementsByClassName(search_input.getAttribute('data-table'));
|
||||
myArray.forEach.call(tables, function(table) {
|
||||
myArray.forEach.call(table.tBodies, function(tbody) {
|
||||
myArray.forEach.call(tbody.rows, function(row) {
|
||||
var text_content = row.textContent.toLowerCase();
|
||||
var search_val = search_input.value.toLowerCase();
|
||||
row.style.display = text_content.indexOf(search_val) > -1 ? '' : 'none';
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
init: function() {
|
||||
var inputs = document.getElementsByClassName('search-input');
|
||||
myArray.forEach.call(inputs, function(input) {
|
||||
input.oninput = _onInputSearch;
|
||||
});
|
||||
}
|
||||
};
|
||||
})(Array.prototype);
|
||||
|
||||
document.addEventListener('readystatechange', function() {
|
||||
if (document.readyState === 'complete') {
|
||||
TableFilter.init();
|
||||
}
|
||||
});
|
||||
|
||||
})(document);
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="wrapper">
|
||||
{% include 'header.j2' %}
|
||||
<section>
|
||||
<center>
|
||||
<h1>Ansible Windows Automation Report</h1>
|
||||
<h3><input type="search" placeholder="Search..." class="form-control search-input" data-table="main_net_table"/>
|
||||
</center>
|
||||
<table class="table table-striped mt32 main_net_table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Windows Device</th>
|
||||
<th>Operating System</th>
|
||||
<th>Operating System Kernel Version</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for windows_host in groups['tag_Windows']|sort %}
|
||||
<tr>
|
||||
<td class="summary_info">
|
||||
<div id="hostname">
|
||||
<p class="hostname">
|
||||
<img class="router_image" src="server.png"> {{ hostvars[windows_host]['inventory_hostname'].split('.')[0] }}</p>
|
||||
</div>
|
||||
{% if detailedreport == 'True' %}
|
||||
{% include 'packages.j2' %}
|
||||
{% include 'services.j2' %}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{hostvars[windows_host]['ansible_os_family']|default("none")}}</td>
|
||||
<td>{{hostvars[windows_host]['ansible_distribution']|default("none")}}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<center><p>Created with</p><br><img src="webpage_logo.png" width="300">
|
||||
</center>
|
||||
</section>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,29 @@
|
||||
<!–– INTERNAL TABLE FOR SERVICES --!>
|
||||
<div id="accordion">
|
||||
<div class="ui-accordion ui-widget ui-helper-reset" role="tablist">
|
||||
<h3 class="ui-accordion-header ui-corner-top ui-state-default ui-accordion-icons ui-accordion-header-collapsed ui-corner-all" role="tab" id="ui-id-3" aria-controls="ui-id-4" aria-selected="false" aria-expanded="false" tabindex="0">Services Facts</h3>
|
||||
<div class="net_content ui-accordion-content ui-corner-bottom ui-helper-reset ui-widget-content" id="ui-id-4" aria-labelledby="ui-id-3" role="tabpanel" aria-hidden="true" style="display: none; height: 194px;">
|
||||
<table id="subtable" class="sortable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Display Name</th>
|
||||
<th>Windows Services Name</th>
|
||||
<th>State</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% if hostvars[windows_host]['services'] is defined %}
|
||||
{% for service in hostvars[windows_host]['services'] %}
|
||||
<tr>
|
||||
<td>{{service['name']}}</td>
|
||||
<td>{{service['win_svc_name']}}</td>
|
||||
<td>{{service['state']}}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!–– END INTERNAL TABLE FOR SERVICES --!>
|
||||
@@ -0,0 +1 @@
|
||||
file_path: /var/www/html
|
||||
@@ -0,0 +1,36 @@
|
||||
build_report_windows_patch
|
||||
========
|
||||
|
||||
Installs Apache and creates a report based on facts from Windows update job
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
Must run on Apache server
|
||||
|
||||
Role Variables / Configuration
|
||||
--------------
|
||||
|
||||
N/A
|
||||
|
||||
Dependencies
|
||||
------------
|
||||
|
||||
N/A
|
||||
|
||||
Example Playbook
|
||||
----------------
|
||||
|
||||
The role can be used to create an html patching report on any number of Linux hosts using any number of Windows servers
|
||||
|
||||
|
||||
```
|
||||
---
|
||||
- hosts: all
|
||||
|
||||
tasks:
|
||||
- name: Run Windows Patch Report
|
||||
import_role:
|
||||
name: shadowman.reports.build_report_windows_patch
|
||||
|
||||
```
|
||||
@@ -0,0 +1,3 @@
|
||||
EMAIL_FROM: tower@shadowman.dev
|
||||
to_emails: alex@shadowman.dev,tower@shadowman.dev
|
||||
EMAIL_TO: "{{ to_emails.split(',') }}"
|
||||
|
After Width: | Height: | Size: 111 KiB |
@@ -0,0 +1,111 @@
|
||||
p.hostname {
|
||||
color: #000000;
|
||||
font-weight: bolder;
|
||||
font-size: large;
|
||||
}
|
||||
|
||||
#subtable {
|
||||
background: #ebebeb;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
#subtable tbody tr td {
|
||||
padding: 5px 5px 5px 5px;
|
||||
}
|
||||
|
||||
#subtable thead th {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
* {
|
||||
-moz-box-sizing: border-box;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
font-family: "Open Sans", "Helvetica";
|
||||
|
||||
}
|
||||
|
||||
a {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
p {
|
||||
color: #ffffff;
|
||||
}
|
||||
h1 {
|
||||
text-align: center;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
body {
|
||||
background:#353a40;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: separate;
|
||||
background:#fff;
|
||||
@include border-radius(5px);
|
||||
margin:50px auto;
|
||||
@include box-shadow(0px 0px 5px rgba(0,0,0,0.3));
|
||||
}
|
||||
|
||||
thead {
|
||||
@include border-radius(5px);
|
||||
}
|
||||
|
||||
thead th {
|
||||
font-family: 'Patua One', monospace;
|
||||
font-size:16px;
|
||||
font-weight:400;
|
||||
color:#fff;
|
||||
@include text-shadow(1px 1px 0px rgba(0,0,0,0.5));
|
||||
text-align:left;
|
||||
padding:20px;
|
||||
border-top:1px solid #858d99;
|
||||
background: #353a40;
|
||||
|
||||
&:first-child {
|
||||
@include border-top-left-radius(5px);
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
@include border-top-right-radius(5px);
|
||||
}
|
||||
}
|
||||
|
||||
tbody tr td {
|
||||
font-family: 'Open Sans', sans-serif;
|
||||
font-weight:400;
|
||||
color:#5f6062;
|
||||
font-size:13px;
|
||||
padding:20px 20px 20px 20px;
|
||||
border-bottom:1px solid #e0e0e0;
|
||||
|
||||
}
|
||||
|
||||
tbody tr:nth-child(2n) {
|
||||
background:#f0f3f5;
|
||||
}
|
||||
|
||||
tbody tr:last-child td {
|
||||
border-bottom:none;
|
||||
&:first-child {
|
||||
@include border-bottom-left-radius(5px);
|
||||
}
|
||||
&:last-child {
|
||||
@include border-bottom-right-radius(5px);
|
||||
}
|
||||
}
|
||||
|
||||
span.highlight {
|
||||
background-color: yellow;
|
||||
}
|
||||
|
||||
.expandclass {
|
||||
color: #5f6062;
|
||||
}
|
||||
|
||||
.content{
|
||||
display:none;
|
||||
margin: 10px;
|
||||
}
|
||||
|
After Width: | Height: | Size: 35 KiB |
@@ -0,0 +1,38 @@
|
||||
- name: Create HTML report
|
||||
ansible.builtin.template:
|
||||
src: report.j2
|
||||
dest: "{{ file_path }}/windowspatch.html"
|
||||
check_mode: no
|
||||
|
||||
- name: Copy CSS over
|
||||
ansible.builtin.copy:
|
||||
src: "css"
|
||||
dest: "{{ file_path }}"
|
||||
directory_mode: true
|
||||
check_mode: no
|
||||
|
||||
- name: Copy logo over
|
||||
ansible.builtin.copy:
|
||||
src: "webpage_logo.png"
|
||||
dest: "{{ file_path }}"
|
||||
directory_mode: true
|
||||
check_mode: no
|
||||
|
||||
- name: Display link to Patch report
|
||||
ansible.builtin.debug:
|
||||
msg: "Please go to http://{{ ansible_host }}/windowspatch.html"
|
||||
|
||||
- name: Send Report via E-mail
|
||||
community.general.mail:
|
||||
host: "{{ EMAIL_HOST }}"
|
||||
username: "{{ EMAIL_USERNAME }}"
|
||||
password: "{{ EMAIL_PASSWORD }}"
|
||||
port: "{{ EMAIL_PORT }}"
|
||||
subject: "Windows Patching Report"
|
||||
body: "{{ lookup('template', 'report.j2') }}"
|
||||
from: "{{ EMAIL_FROM }}"
|
||||
to: "{{ EMAIL_TO }}"
|
||||
subtype: html
|
||||
delegate_to: localhost
|
||||
become: false
|
||||
check_mode: no
|
||||
@@ -0,0 +1,115 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title> Windows Patch Report </title>
|
||||
</head>
|
||||
<body>
|
||||
<center>
|
||||
<h1>Ansible Windows Patching Report</h1>
|
||||
<style>
|
||||
@media print {
|
||||
.noprint {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<div class="noprint">
|
||||
<button type="button" onclick="tableToCSV()">Download CSV</button>
|
||||
<input type="button" value="Print" onClick="window.print()">
|
||||
</div>
|
||||
</center>
|
||||
<table border = "1" cellpadding = "5" cellspacing = "5">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Hostname</th>
|
||||
<th>Operating System</th>
|
||||
<th>Required Updates</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for windows_host in groups['tag_Windows']|sort %}
|
||||
<tr>
|
||||
<td>{{hostvars[windows_host]['inventory_hostname']}}</td>
|
||||
<td>{{hostvars[windows_host]['ansible_distribution']|default("none")}}</td>
|
||||
<td>
|
||||
<ul>
|
||||
|
||||
{% if hostvars[windows_host].patchresult.updates is defined and hostvars[windows_host].patchresult.found_update_count|int > 0 %}
|
||||
{% for update in hostvars[windows_host].patchresult.updates %}
|
||||
{% set updatenum = hostvars[windows_host].patchresult.updates[update] %}
|
||||
<li>{{updatenum.title}}</li>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<li>Compliant</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% for host in ansible_play_hosts %}
|
||||
<center><p>Created with Ansible on {{hostvars[host].ansible_date_time.iso8601}}</p></center>
|
||||
{% endfor %}
|
||||
<script type="text/javascript">
|
||||
function tableToCSV() {
|
||||
|
||||
// Variable to store the final csv data
|
||||
var csv_data = [];
|
||||
|
||||
// Get each row data
|
||||
var rows = document.getElementsByTagName('tr');
|
||||
for (var i = 0; i < rows.length; i++) {
|
||||
|
||||
// Get each column data
|
||||
var cols = rows[i].querySelectorAll('td,th');
|
||||
|
||||
// Stores each csv row data
|
||||
var csvrow = [];
|
||||
for (var j = 0; j < (cols.length); j++) {
|
||||
|
||||
// Get the text data of each cell of
|
||||
// a row and push it to csvrow
|
||||
if ( j == cols.length-1 && i==0){}
|
||||
else{
|
||||
csvrow.push(cols[j].textContent.replace(/,/g, " "));
|
||||
}
|
||||
|
||||
}
|
||||
csv_data.push(csvrow.join(","));
|
||||
}
|
||||
|
||||
// combine each row data with new line character
|
||||
csv_data = csv_data.join('\n');
|
||||
|
||||
// Call this function to download csv file
|
||||
downloadCSVFile(csv_data);
|
||||
}
|
||||
function downloadCSVFile(csv_data) {
|
||||
|
||||
// Create CSV file object and feed our
|
||||
// csv_data into it
|
||||
CSVFile = new Blob([csv_data], { type: "text/csv" });
|
||||
|
||||
// Create to temporary link to initiate
|
||||
// download process
|
||||
var temp_link = document.createElement('a');
|
||||
var todayDate = new Date().toISOString().slice(0, 10);
|
||||
|
||||
// Download csv file
|
||||
temp_link.download = "windowspatching-" + todayDate + ".csv";
|
||||
var url = window.URL.createObjectURL(CSVFile);
|
||||
temp_link.href = url;
|
||||
|
||||
// This link should not be displayed
|
||||
temp_link.style.display = "none";
|
||||
document.body.appendChild(temp_link);
|
||||
|
||||
// Automatically click the link to trigger download
|
||||
temp_link.click();
|
||||
document.body.removeChild(temp_link);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1 @@
|
||||
file_path: /var/www/html
|
||||
@@ -0,0 +1,3 @@
|
||||
---
|
||||
exclude_packages: []
|
||||
allow_reboot: true
|
||||
@@ -0,0 +1,38 @@
|
||||
---
|
||||
- name: Scan packages
|
||||
demo.patching.scan_packages:
|
||||
os_family: "{{ ansible_os_family }}"
|
||||
check_mode: no
|
||||
|
||||
- name: Scan services
|
||||
demo.patching.scan_services:
|
||||
check_mode: no
|
||||
|
||||
- name: upgrade packages (yum)
|
||||
yum:
|
||||
name: '*'
|
||||
state: latest
|
||||
exclude: "{{ exclude_packages }}"
|
||||
when: ansible_pkg_mgr == "yum"
|
||||
register: patchingresult_yum
|
||||
|
||||
- name: upgrade packages (dnf)
|
||||
ansible.builtin.dnf:
|
||||
name: '*'
|
||||
state: latest
|
||||
exclude: "{{ exclude_packages }}"
|
||||
when: ansible_pkg_mgr == "dnf"
|
||||
register: patchingresult_dnf
|
||||
|
||||
- name: Check to see if we need a reboot
|
||||
ansible.builtin.command: needs-restarting -r
|
||||
register: result
|
||||
changed_when: result.rc == 1
|
||||
failed_when: result.rc > 1
|
||||
check_mode: no
|
||||
|
||||
- name: Reboot Server if Necessary
|
||||
ansible.builtin.reboot:
|
||||
when:
|
||||
- result.rc == 1
|
||||
- allow_reboot == true
|
||||
@@ -0,0 +1,36 @@
|
||||
build_report_linux
|
||||
========
|
||||
|
||||
Installs Apache and creates a report based on facts from Linux services and packages modules
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
Must run on Apache server
|
||||
|
||||
Role Variables / Configuration
|
||||
--------------
|
||||
|
||||
N/A
|
||||
|
||||
Dependencies
|
||||
------------
|
||||
|
||||
N/A
|
||||
|
||||
Example Playbook
|
||||
----------------
|
||||
|
||||
The role can be used to create an html report on any number of Linux hosts using any number of Linux servers about their services and packages installed
|
||||
|
||||
|
||||
```
|
||||
---
|
||||
- hosts: all
|
||||
|
||||
tasks:
|
||||
- name: Run Linux Report
|
||||
import_role:
|
||||
name: shadowman.reports.build_report_linux
|
||||
|
||||
```
|
||||
@@ -0,0 +1,2 @@
|
||||
---
|
||||
detailedreport: True
|
||||
|
After Width: | Height: | Size: 65 KiB |
@@ -0,0 +1,202 @@
|
||||
p.hostname {
|
||||
color: #000000;
|
||||
font-weight: bolder;
|
||||
font-size: large;
|
||||
margin: auto;
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
#subtable {
|
||||
background: #ebebeb;
|
||||
margin: 0px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#subtable tbody tr td {
|
||||
padding: 5px 5px 5px 5px;
|
||||
}
|
||||
|
||||
#subtable thead th {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
* {
|
||||
-moz-box-sizing: border-box;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
font-family: "Open Sans", "Helvetica";
|
||||
|
||||
}
|
||||
|
||||
a {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
p {
|
||||
color: #ffffff;
|
||||
}
|
||||
h1 {
|
||||
text-align: center;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
body {
|
||||
background:#353a40;
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: separate;
|
||||
background:#fff;
|
||||
@include border-radius(5px);
|
||||
@include box-shadow(0px 0px 5px rgba(0,0,0,0.3));
|
||||
}
|
||||
|
||||
.main_net_table {
|
||||
margin:50px auto;
|
||||
}
|
||||
|
||||
thead {
|
||||
@include border-radius(5px);
|
||||
}
|
||||
|
||||
thead th {
|
||||
font-size:16px;
|
||||
font-weight:400;
|
||||
color:#fff;
|
||||
@include text-shadow(1px 1px 0px rgba(0,0,0,0.5));
|
||||
text-align:left;
|
||||
padding:20px;
|
||||
border-top:1px solid #858d99;
|
||||
background: #353a40;
|
||||
|
||||
&:first-child {
|
||||
@include border-top-left-radius(5px);
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
@include border-top-right-radius(5px);
|
||||
}
|
||||
}
|
||||
|
||||
tbody tr td {
|
||||
font-weight:400;
|
||||
color:#5f6062;
|
||||
font-size:13px;
|
||||
padding:20px 20px 20px 20px;
|
||||
border-bottom:1px solid #e0e0e0;
|
||||
}
|
||||
|
||||
tbody tr:nth-child(2n) {
|
||||
background:#f0f3f5;
|
||||
}
|
||||
|
||||
tbody tr:last-child td {
|
||||
border-bottom:none;
|
||||
&:first-child {
|
||||
@include border-bottom-left-radius(5px);
|
||||
}
|
||||
&:last-child {
|
||||
@include border-bottom-right-radius(5px);
|
||||
}
|
||||
}
|
||||
|
||||
td {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
span.highlight {
|
||||
background-color: yellow;
|
||||
}
|
||||
|
||||
.expandclass {
|
||||
color: #5f6062;
|
||||
}
|
||||
|
||||
.content{
|
||||
display:none;
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
header {
|
||||
width: 100%;
|
||||
position: initial;
|
||||
float: initial;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
border-radius: 0;
|
||||
height: 88px;
|
||||
background-color: #171717;
|
||||
}
|
||||
|
||||
.header-container {
|
||||
margin: 0 auto;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
max-width: 1170px;
|
||||
padding: 0;
|
||||
float: initial;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.header-logo {
|
||||
width: 137px;
|
||||
border: 0;
|
||||
margin: 0;
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
.header-link {
|
||||
margin-left: 40px;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
text-transform: uppercase;
|
||||
font-size: 15px;
|
||||
font-family: 'Red Hat Text';
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.header-link:hover {
|
||||
text-shadow: 0 0 0.02px white;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
table.net_info td {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
p.expandclass:hover {
|
||||
text-decoration: underline;
|
||||
color: #EE0000;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.summary_info {
|
||||
}
|
||||
|
||||
.ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active, a.ui-button:active, .ui-button:active, .ui-button.ui-state-active:hover {
|
||||
border: 1px solid #5F0000;
|
||||
background: #EE0000;
|
||||
}
|
||||
|
||||
div#net_content {
|
||||
padding: 0px;
|
||||
height: auto !important;
|
||||
}
|
||||
|
||||
img.router_image {
|
||||
vertical-align: middle;
|
||||
padding: 0px 10px 10px 10px;
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
table.net_info {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
p.internal_label {
|
||||
color: #000000;
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 24.0.3, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Logos" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="930.2px" height="350px" viewBox="0 0 930.2 350" style="enable-background:new 0 0 930.2 350;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:#FFFFFF;}
|
||||
.st1{fill:#EE0000;}
|
||||
</style>
|
||||
<title>Logo-Red_Hat-Ansible_Automation_Platform-A-Reverse-RGB</title>
|
||||
<path class="st0" d="M383.3,228.5h18.8L446,335.7h-17.5l-12.4-31.4h-48l-12.6,31.4h-16.7L383.3,228.5z M410.9,291l-18.7-47l-18.7,47
|
||||
H410.9z"/>
|
||||
<path class="st0" d="M455.2,257.7h15.3v7.8c6.2-6.2,14.7-9.6,23.5-9.3c17.9,0,30.5,12.4,30.5,30.5v49h-15.3v-46.5
|
||||
c0-12.3-7.5-19.8-19.3-19.8c-7.8-0.3-15.1,3.6-19.3,10.1v56.1h-15.3V257.7z"/>
|
||||
<path class="st0" d="M543,315.5c8.1,6.4,16.7,9.8,25.4,9.8c11,0,18.7-4.8,18.7-11.7c0-5.5-4-8.7-12.6-10l-14.1-2
|
||||
c-15.5-2.3-23.3-9.5-23.3-21.6c0-14.1,12.3-23.6,30.5-23.6c11.3-0.1,22.3,3.4,31.5,9.9l-7.8,10.1c-8.6-5.7-16.4-8.1-24.7-8.1
|
||||
c-9.3,0-15.6,4.3-15.6,10.6c0,5.7,3.7,8.4,12.9,9.8l14.1,2c15.5,2.3,23.6,9.7,23.6,21.7c0,14-14.1,24.5-32.6,24.5
|
||||
c-13.5,0-25.6-4-34.2-11.5L543,315.5z"/>
|
||||
<path class="st0" d="M611.6,235.6c0-5.2,4.1-9.4,9.3-9.5c0,0,0,0,0,0c5.2-0.2,9.7,3.9,9.9,9.1c0.2,5.2-3.9,9.7-9.1,9.9
|
||||
c-0.2,0-0.5,0-0.7,0C615.8,245.1,611.6,240.9,611.6,235.6C611.6,235.7,611.6,235.7,611.6,235.6z M628.6,335.7h-15.3v-78h15.3V335.7z
|
||||
"/>
|
||||
<path class="st0" d="M685.5,336.9c-8.5,0-16.8-2.7-23.6-7.8v6.6h-15.2V228.5l15.3-3.4v40c6.6-5.6,15.1-8.7,23.7-8.6
|
||||
c22.1,0,39.4,17.7,39.4,40.1C725.2,319.1,707.9,336.9,685.5,336.9z M662,279.2v35.2c4.9,5.7,13,9.2,21.8,9.2
|
||||
c15,0,26.4-11.5,26.4-26.8c0-15.3-11.5-27-26.4-27C674.9,269.8,667.1,273.2,662,279.2z"/>
|
||||
<path class="st0" d="M755,335.7h-15.3V228.5l15.3-3.4V335.7z"/>
|
||||
<path class="st0" d="M810.5,337.1c-23,0-40.9-17.7-40.9-40.4c0-22.5,17.2-40.1,39.1-40.1c21.5,0,37.7,17.8,37.7,40.8v4.4h-61.6
|
||||
c2,13,13.2,22.5,26.4,22.4c7.2,0.2,14.2-2.3,19.8-6.8l9.8,9.7C832.1,333.7,821.5,337.4,810.5,337.1z M784.9,290.2h46.3
|
||||
c-2.3-11.9-11.5-20.8-22.8-20.8C796.5,269.4,787.2,277.8,784.9,290.2z"/>
|
||||
<path class="st1" d="M202.8,137.5c18.4,0,45.1-3.8,45.1-25.7c0.1-1.7-0.1-3.4-0.5-5l-11-47.7c-2.5-10.5-4.8-15.2-23.2-24.5
|
||||
c-14.3-7.3-45.5-19.4-54.7-19.4c-8.6,0-11.1,11.1-21.3,11.1c-9.8,0-17.1-8.3-26.4-8.3c-8.8,0-14.6,6-19,18.4c0,0-12.4,34.9-14,40
|
||||
c-0.3,0.9-0.4,1.9-0.4,2.9C77.6,92.9,131.1,137.5,202.8,137.5 M250.8,120.7c2.5,12.1,2.5,13.3,2.5,14.9c0,20.6-23.2,32.1-53.7,32.1
|
||||
c-69,0-129.3-40.3-129.3-67c0-3.7,0.8-7.4,2.2-10.8c-24.8,1.3-56.9,5.7-56.9,34c0,46.4,109.9,103.5,196.9,103.5
|
||||
c66.7,0,83.5-30.2,83.5-54C296.1,154.6,279.9,133.4,250.8,120.7"/>
|
||||
<path d="M250.7,120.7c2.5,12.1,2.5,13.3,2.5,14.9c0,20.6-23.2,32.1-53.7,32.1c-69,0-129.3-40.3-129.3-67c0-3.7,0.8-7.4,2.2-10.8
|
||||
l5.4-13.3c-0.3,0.9-0.4,1.9-0.4,2.8c0,13.6,53.5,58.1,125.2,58.1c18.4,0,45.1-3.8,45.1-25.7c0.1-1.7-0.1-3.4-0.5-5L250.7,120.7z"/>
|
||||
<path class="st0" d="M869.1,151.2c0,17.5,10.5,26,29.7,26c5.9-0.1,11.8-1,17.5-2.5v-20.3c-3.7,1.2-7.5,1.7-11.3,1.7
|
||||
c-7.9,0-10.8-2.5-10.8-9.9v-31.1h22.9V94.2h-22.9V67.7l-25,5.4v21.1h-16.6v20.9h16.6L869.1,151.2z M791,151.7
|
||||
c0-5.4,5.4-8.1,13.6-8.1c5,0,10,0.7,14.9,1.9V156c-4.8,2.6-10.2,3.9-15.6,3.9C795.9,159.9,791.1,156.8,791,151.7 M798.7,177.5
|
||||
c8.8,0,16-1.9,22.6-6.3v5h24.8v-52.5c0-20-13.5-30.9-35.9-30.9c-12.6,0-25,2.9-38.3,9l9,18.4c9.6-4,17.7-6.5,24.8-6.5
|
||||
c10.3,0,15.6,4,15.6,12.2v4c-6.1-1.6-12.3-2.4-18.6-2.3c-21.1,0-33.8,8.8-33.8,24.6C768.9,166.6,780.4,177.6,798.7,177.5
|
||||
M662.5,176.2h26.7v-42.5h44.6v42.5h26.7V67.7h-26.6v41.7h-44.6V67.7h-26.7L662.5,176.2z M561,135.1c0-11.8,9.3-20.8,21.5-20.8
|
||||
c6.4-0.1,12.6,2.1,17.4,6.4v28.6c-4.7,4.4-10.9,6.7-17.4,6.5C570.5,155.8,561,146.8,561,135.1 M600.2,176.1H625V62.3l-25,5.4v30.8
|
||||
c-6.4-3.6-13.6-5.5-20.9-5.4c-23.9,0-42.6,18.4-42.6,42c-0.3,23,18.1,41.9,41.1,42.2c0.2,0,0.5,0,0.7,0c7.9,0,15.6-2.5,22-7.1V176.1
|
||||
z M486.5,113.2c7.9,0,14.6,5.1,17.2,13h-34.2C471.9,118,478.2,113.2,486.5,113.2 M444.2,135.2c0,23.9,19.5,42.5,44.6,42.5
|
||||
c13.8,0,23.9-3.7,34.3-12.4l-16.6-14.7c-3.9,4-9.6,6.2-16.4,6.2c-8.8,0.2-16.8-4.9-20.2-13h58.4v-6.2c0-26-17.5-44.8-41.4-44.8
|
||||
c-23.2-0.4-42.4,18.2-42.7,41.5C444.2,134.6,444.2,134.9,444.2,135.2 M400.9,90.5c8.8,0,13.8,5.6,13.8,12.2s-5,12.2-13.8,12.2h-26.3
|
||||
V90.5H400.9z M347.9,176.2h26.7v-39.5h20.3l20.5,39.5h29.7l-23.9-43.4c12.4-5,20.5-17.1,20.4-30.5c0-19.5-15.3-34.5-38.3-34.5H348
|
||||
L347.9,176.2z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 45 KiB |
@@ -0,0 +1,27 @@
|
||||
- name: create HTML report
|
||||
ansible.builtin.template:
|
||||
src: report.j2
|
||||
dest: "{{ file_path }}/linux.html"
|
||||
check_mode: no
|
||||
|
||||
- name: copy CSS over
|
||||
ansible.builtin.copy:
|
||||
src: "css"
|
||||
dest: "{{ file_path }}"
|
||||
directory_mode: true
|
||||
check_mode: no
|
||||
|
||||
- name: copy logos over
|
||||
ansible.builtin.copy:
|
||||
src: "{{ item }}"
|
||||
dest: "{{ file_path }}"
|
||||
directory_mode: true
|
||||
loop:
|
||||
- "webpage_logo.png"
|
||||
- "redhat-ansible-logo.svg"
|
||||
- "server.png"
|
||||
check_mode: no
|
||||
|
||||
- name: display link to inventory report
|
||||
ansible.builtin.debug:
|
||||
msg: "Please go to http://{{ ansible_host }}/reports/linux.html"
|
||||
@@ -0,0 +1,15 @@
|
||||
|
||||
|
||||
<div class="wrapper">
|
||||
<header>
|
||||
<div class="header-container">
|
||||
<a href="https://ansible.com">
|
||||
<img
|
||||
class="header-logo"
|
||||
src="redhat-ansible-logo.svg"
|
||||
title="Red Hat Ansible"
|
||||
alt="Red Hat Ansible"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
</header>
|
||||
@@ -0,0 +1,31 @@
|
||||
<!–– INTERNAL TABLE FOR PACKAGES --!>
|
||||
<div id="accordion">
|
||||
<div class="ui-accordion ui-widget ui-helper-reset" role="tablist">
|
||||
<h3 class="ui-accordion-header ui-corner-top ui-state-default ui-accordion-icons ui-accordion-header-collapsed ui-corner-all" role="tab" id="ui-id-3" aria-controls="ui-id-4" aria-selected="false" aria-expanded="false" tabindex="0">Package Facts</h3>
|
||||
<div class="net_content ui-accordion-content ui-corner-bottom ui-helper-reset ui-widget-content" id="ui-id-4" aria-labelledby="ui-id-3" role="tabpanel" aria-hidden="true" style="display: none; height: 194px;">
|
||||
<table id="subtable" class="sortable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Package Name</th>
|
||||
<th>source</th>
|
||||
<th>release</th>
|
||||
<th>version</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% if hostvars[linux_host]['packages'] is defined %}
|
||||
{% for package in hostvars[linux_host]['packages'] %}
|
||||
<tr>
|
||||
<td>{{package['name']}}</td>
|
||||
<td>{{package['source']}}</td>
|
||||
<td>{{package['release']}}</td>
|
||||
<td>{{package['version']}}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!–– END INTERNAL TABLE FOR PACKAGES --!>
|
||||
@@ -0,0 +1,105 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title> Ansible Linux Automation Report </title>
|
||||
<link rel="stylesheet" type="text/css" href="//fonts.googleapis.com/css?family=Open+Sans" />
|
||||
<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
|
||||
<link rel="stylesheet" href="css/new.css">
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
|
||||
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
|
||||
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
|
||||
<script src="https://www.kryogenix.org/code/browser/sorttable/sorttable.js"></script>
|
||||
<script>
|
||||
$(function() {
|
||||
$( "#accordion > div" ).accordion({
|
||||
header: "h3",
|
||||
active: false,
|
||||
collapsible: true
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<script>
|
||||
(function(document) {
|
||||
'use strict';
|
||||
|
||||
var TableFilter = (function(myArray) {
|
||||
var search_input;
|
||||
|
||||
function _onInputSearch(e) {
|
||||
search_input = e.target;
|
||||
var tables = document.getElementsByClassName(search_input.getAttribute('data-table'));
|
||||
myArray.forEach.call(tables, function(table) {
|
||||
myArray.forEach.call(table.tBodies, function(tbody) {
|
||||
myArray.forEach.call(tbody.rows, function(row) {
|
||||
var text_content = row.textContent.toLowerCase();
|
||||
var search_val = search_input.value.toLowerCase();
|
||||
row.style.display = text_content.indexOf(search_val) > -1 ? '' : 'none';
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
init: function() {
|
||||
var inputs = document.getElementsByClassName('search-input');
|
||||
myArray.forEach.call(inputs, function(input) {
|
||||
input.oninput = _onInputSearch;
|
||||
});
|
||||
}
|
||||
};
|
||||
})(Array.prototype);
|
||||
|
||||
document.addEventListener('readystatechange', function() {
|
||||
if (document.readyState === 'complete') {
|
||||
TableFilter.init();
|
||||
}
|
||||
});
|
||||
|
||||
})(document);
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="wrapper">
|
||||
{% include 'header.j2' %}
|
||||
<section>
|
||||
<center>
|
||||
<h1>Ansible Linux Automation Report</h1>
|
||||
<h3><input type="search" placeholder="Search..." class="form-control search-input" data-table="main_net_table"/>
|
||||
</center>
|
||||
<table class="table table-striped mt32 main_net_table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Linux Device</th>
|
||||
<th>Package Manager</th>
|
||||
<th>Operating System</th>
|
||||
<th>Operating System Version</th>
|
||||
<th>Operating System Kernel Version</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for linux_host in ansible_play_hosts |sort %}
|
||||
<tr>
|
||||
<td class="summary_info">
|
||||
<div id="hostname">
|
||||
<p class="hostname">
|
||||
<img class="router_image" src="server.png"> {{ hostvars[linux_host]['inventory_hostname'].split('.')[0] }}</p>
|
||||
</div>
|
||||
{% if detailedreport == 'True' %}
|
||||
{% include 'packages.j2' %}
|
||||
{% include 'services.j2' %}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{hostvars[linux_host]['ansible_pkg_mgr']|default("none")}}</td>
|
||||
<td>{{hostvars[linux_host]['ansible_os_family']|default("none")}}</td>
|
||||
<td>{{hostvars[linux_host]['ansible_distribution_version']|default("none")}}</td>
|
||||
<td>{{hostvars[linux_host]['ansible_kernel']|default("none")}}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<center><p>Created with</p><br><img src="webpage_logo.png" width="300">
|
||||
</center>
|
||||
</section>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,30 @@
|
||||
<!–– INTERNAL TABLE FOR SERVICES --!>
|
||||
<div id="accordion">
|
||||
<div class="ui-accordion ui-widget ui-helper-reset" role="tablist">
|
||||
<h3 class="ui-accordion-header ui-corner-top ui-state-default ui-accordion-icons ui-accordion-header-collapsed ui-corner-all" role="tab" id="ui-id-3" aria-controls="ui-id-4" aria-selected="false" aria-expanded="false" tabindex="0">Services Facts</h3>
|
||||
<div class="net_content ui-accordion-content ui-corner-bottom ui-helper-reset ui-widget-content" id="ui-id-4" aria-labelledby="ui-id-3" role="tabpanel" aria-hidden="true" style="display: none; height: 194px;">
|
||||
<table id="subtable" class="sortable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Service Name</th>
|
||||
<th>State</th>
|
||||
<th>Source</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% if hostvars[linux_host]['services'] is defined %}
|
||||
{% for servicesname in hostvars[linux_host]['services']|sort %}
|
||||
{% set service = hostvars[linux_host]['services'][servicesname] %}
|
||||
<tr>
|
||||
<td>{{service['name']}}</td>
|
||||
<td>{{service['state']}}</td>
|
||||
<td>{{service['source']}}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!–– END INTERNAL TABLE FOR SERVICES --!>
|
||||
@@ -0,0 +1 @@
|
||||
file_path: /var/www/html/reports
|
||||
@@ -0,0 +1,36 @@
|
||||
build_report_linux_patch
|
||||
========
|
||||
|
||||
Installs Apache and creates a report based on facts from Linux patching
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
Must run on Apache server
|
||||
|
||||
Role Variables / Configuration
|
||||
--------------
|
||||
|
||||
N/A
|
||||
|
||||
Dependencies
|
||||
------------
|
||||
|
||||
N/A
|
||||
|
||||
Example Playbook
|
||||
----------------
|
||||
|
||||
The role can be used to create an html report on any number of Linux hosts using any number of Linux servers about their patching results(yum and dnf)
|
||||
|
||||
|
||||
```
|
||||
---
|
||||
- hosts: all
|
||||
|
||||
tasks:
|
||||
- name: Run Windows Report
|
||||
import_role:
|
||||
name: shadowman.reports.build_report_linux_patch
|
||||
|
||||
```
|
||||
@@ -0,0 +1,3 @@
|
||||
EMAIL_FROM: tower@shadowman.dev
|
||||
to_emails: alex@shadowman.dev,tower@shadowman.dev
|
||||
EMAIL_TO: "{{ to_emails.split(',') }}"
|
||||
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 108 KiB |
@@ -0,0 +1,111 @@
|
||||
p.hostname {
|
||||
color: #000000;
|
||||
font-weight: bolder;
|
||||
font-size: large;
|
||||
}
|
||||
|
||||
#subtable {
|
||||
background: #ebebeb;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
#subtable tbody tr td {
|
||||
padding: 5px 5px 5px 5px;
|
||||
}
|
||||
|
||||
#subtable thead th {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
* {
|
||||
-moz-box-sizing: border-box;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
font-family: "Open Sans", "Helvetica";
|
||||
|
||||
}
|
||||
|
||||
a {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
p {
|
||||
color: #ffffff;
|
||||
}
|
||||
h1 {
|
||||
text-align: center;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
body {
|
||||
background:#353a40;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: separate;
|
||||
background:#fff;
|
||||
@include border-radius(5px);
|
||||
margin:50px auto;
|
||||
@include box-shadow(0px 0px 5px rgba(0,0,0,0.3));
|
||||
}
|
||||
|
||||
thead {
|
||||
@include border-radius(5px);
|
||||
}
|
||||
|
||||
thead th {
|
||||
font-family: 'Patua One', monospace;
|
||||
font-size:16px;
|
||||
font-weight:400;
|
||||
color:#fff;
|
||||
@include text-shadow(1px 1px 0px rgba(0,0,0,0.5));
|
||||
text-align:left;
|
||||
padding:20px;
|
||||
border-top:1px solid #858d99;
|
||||
background: #353a40;
|
||||
|
||||
&:first-child {
|
||||
@include border-top-left-radius(5px);
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
@include border-top-right-radius(5px);
|
||||
}
|
||||
}
|
||||
|
||||
tbody tr td {
|
||||
font-family: 'Open Sans', sans-serif;
|
||||
font-weight:400;
|
||||
color:#5f6062;
|
||||
font-size:13px;
|
||||
padding:20px 20px 20px 20px;
|
||||
border-bottom:1px solid #e0e0e0;
|
||||
|
||||
}
|
||||
|
||||
tbody tr:nth-child(2n) {
|
||||
background:#f0f3f5;
|
||||
}
|
||||
|
||||
tbody tr:last-child td {
|
||||
border-bottom:none;
|
||||
&:first-child {
|
||||
@include border-bottom-left-radius(5px);
|
||||
}
|
||||
&:last-child {
|
||||
@include border-bottom-right-radius(5px);
|
||||
}
|
||||
}
|
||||
|
||||
span.highlight {
|
||||
background-color: yellow;
|
||||
}
|
||||
|
||||
.expandclass {
|
||||
color: #5f6062;
|
||||
}
|
||||
|
||||
.content{
|
||||
display:none;
|
||||
margin: 10px;
|
||||
}
|
||||
|
After Width: | Height: | Size: 35 KiB |
@@ -0,0 +1,38 @@
|
||||
- name: Create HTML report
|
||||
ansible.builtin.template:
|
||||
src: report.j2
|
||||
dest: "{{ file_path }}/linuxpatch.html"
|
||||
check_mode: no
|
||||
|
||||
- name: Copy CSS over
|
||||
ansible.builtin.copy:
|
||||
src: "css"
|
||||
dest: "{{ file_path }}"
|
||||
directory_mode: true
|
||||
check_mode: no
|
||||
|
||||
- name: Copy logo over
|
||||
ansible.builtin.copy:
|
||||
src: "webpage_logo.png"
|
||||
dest: "{{ file_path }}"
|
||||
directory_mode: true
|
||||
check_mode: no
|
||||
|
||||
- name: Display link to Linux patch report
|
||||
ansible.builtin.debug:
|
||||
msg: "Please go to http://{{ ansible_host }}/reports/linuxpatch.html"
|
||||
|
||||
#- name: Send Report via E-mail
|
||||
# community.general.mail:
|
||||
# host: "{{ EMAIL_HOST }}"
|
||||
# username: "{{ EMAIL_USERNAME }}"
|
||||
# password: "{{ EMAIL_PASSWORD }}"
|
||||
# port: "{{ EMAIL_PORT }}"
|
||||
# subject: "Linux Patching Report"
|
||||
# body: "{{ lookup('template', 'report.j2') }}"
|
||||
# from: "{{ EMAIL_FROM }}"
|
||||
# to: "{{ EMAIL_TO }}"
|
||||
# subtype: html
|
||||
# delegate_to: localhost
|
||||
# become: false
|
||||
# check_mode: no
|
||||
@@ -0,0 +1,120 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title> Linux Patch Report </title>
|
||||
</head>
|
||||
<body>
|
||||
<center>
|
||||
<h1>Ansible Linux Patching Report</h1>
|
||||
<style>
|
||||
@media print {
|
||||
.noprint {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<div class="noprint">
|
||||
<button type="button" onclick="tableToCSV()">Download CSV</button>
|
||||
<input type="button" value="Print" onClick="window.print()">
|
||||
</div>
|
||||
</center>
|
||||
<table border = "1" cellpadding = "5" cellspacing = "5">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Hostname</th>
|
||||
<th>Operating System</th>
|
||||
<th>Operating System Version</th>
|
||||
<th>Required Updates</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for linux_host in ansible_play_hosts |sort %}
|
||||
<tr>
|
||||
<td>{{hostvars[linux_host]['inventory_hostname']}}</td>
|
||||
<td>{{hostvars[linux_host]['ansible_os_family']|default("none")}}</td>
|
||||
<td>{{hostvars[linux_host]['ansible_distribution_version']|default("none")}}</td>
|
||||
<td>
|
||||
<ul>
|
||||
{% if hostvars[linux_host].patchingresult_yum.changed|default("false",true) == true %}
|
||||
{% for packagename in hostvars[linux_host].patchingresult_yum.changes.updated|sort %}
|
||||
<li> {{ packagename[0] }} - {{ packagename[1] }} </li>
|
||||
{% endfor %}
|
||||
{% elif hostvars[linux_host].patchingresult_dnf.changed|default("false",true) == true %}
|
||||
{% for packagename in hostvars[linux_host].patchingresult_dnf.results|sort %}
|
||||
<li> {{ packagename }} </li>
|
||||
{% endfor %}
|
||||
{% elif hostvars[linux_host].patchingresult_dnf.changed is undefined %}
|
||||
<li> Patching Failed </li>
|
||||
{% elif hostvars[linux_host].patchingresult_yum.changed is undefined %}
|
||||
<li> Patching Failed </li>
|
||||
{% else %}
|
||||
<li> Compliant </li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<center><p>Created with Ansible on {{hostvars[inventory_hostname].ansible_date_time.iso8601}}</p></center>
|
||||
<script type="text/javascript">
|
||||
function tableToCSV() {
|
||||
|
||||
// Variable to store the final csv data
|
||||
var csv_data = [];
|
||||
|
||||
// Get each row data
|
||||
var rows = document.getElementsByTagName('tr');
|
||||
for (var i = 0; i < rows.length; i++) {
|
||||
|
||||
// Get each column data
|
||||
var cols = rows[i].querySelectorAll('td,th');
|
||||
|
||||
// Stores each csv row data
|
||||
var csvrow = [];
|
||||
for (var j = 0; j < (cols.length); j++) {
|
||||
|
||||
// Get the text data of each cell of
|
||||
// a row and push it to csvrow
|
||||
if ( j == cols.length-1 && i==0){}
|
||||
else{
|
||||
csvrow.push(cols[j].textContent.replace(/,/g, " "));
|
||||
}
|
||||
|
||||
}
|
||||
csv_data.push(csvrow.join(","));
|
||||
}
|
||||
|
||||
// combine each row data with new line character
|
||||
csv_data = csv_data.join('\n');
|
||||
|
||||
// Call this function to download csv file
|
||||
downloadCSVFile(csv_data);
|
||||
}
|
||||
function downloadCSVFile(csv_data) {
|
||||
|
||||
// Create CSV file object and feed our
|
||||
// csv_data into it
|
||||
CSVFile = new Blob([csv_data], { type: "text/csv" });
|
||||
|
||||
// Create to temporary link to initiate
|
||||
// download process
|
||||
var temp_link = document.createElement('a');
|
||||
var todayDate = new Date().toISOString().slice(0, 10);
|
||||
|
||||
// Download csv file
|
||||
temp_link.download = "linuxpatching-" + todayDate + ".csv";
|
||||
var url = window.URL.createObjectURL(CSVFile);
|
||||
temp_link.href = url;
|
||||
|
||||
// This link should not be displayed
|
||||
temp_link.style.display = "none";
|
||||
document.body.appendChild(temp_link);
|
||||
|
||||
// Automatically click the link to trigger download
|
||||
temp_link.click();
|
||||
document.body.removeChild(temp_link);
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1 @@
|
||||
file_path: /var/www/html/reports
|
||||
@@ -1,20 +1,38 @@
|
||||
---
|
||||
- name: Apply Non-kernel Updates
|
||||
hosts: "{{ HOSTS }}"
|
||||
become: true
|
||||
- hosts: "{{ HOSTS }}"
|
||||
become: yes
|
||||
vars:
|
||||
report_server: node1
|
||||
|
||||
tasks:
|
||||
- name: upgrade all packages except kernel
|
||||
yum:
|
||||
name: '*'
|
||||
state: latest
|
||||
exclude: kernel*
|
||||
tags: all
|
||||
- include_role:
|
||||
name: demo.patching.patch_linux
|
||||
|
||||
- name: upgrade all packages security related except kernel
|
||||
yum:
|
||||
name: '*'
|
||||
- block:
|
||||
- yum:
|
||||
name: httpd
|
||||
state: latest
|
||||
security: true
|
||||
exclude: kernel*
|
||||
tags: security
|
||||
check_mode: no
|
||||
|
||||
- file:
|
||||
path: /var/www/html/reports/
|
||||
state: directory
|
||||
check_mode: no
|
||||
|
||||
- copy:
|
||||
dest: /var/www/html/reports/.htaccess
|
||||
content: Options +Indexes
|
||||
check_mode: no
|
||||
|
||||
- service:
|
||||
name: httpd
|
||||
state: started
|
||||
check_mode: no
|
||||
|
||||
- include_role:
|
||||
name: demo.patching.report_linux
|
||||
|
||||
- include_role:
|
||||
name: demo.patching.report_linux_patching
|
||||
delegate_to: "{{ report_server }}"
|
||||
run_once: yes
|
||||
38
linux/patching_report.yml
Normal file
@@ -0,0 +1,38 @@
|
||||
---
|
||||
- hosts: "{{ HOSTS }}"
|
||||
become: yes
|
||||
vars:
|
||||
report_server: node1
|
||||
|
||||
tasks:
|
||||
- include_role:
|
||||
name: demo.patching.patch_linux
|
||||
|
||||
- block:
|
||||
- yum:
|
||||
name: httpd
|
||||
state: latest
|
||||
check_mode: no
|
||||
|
||||
- file:
|
||||
path: /var/www/html/reports/
|
||||
state: directory
|
||||
check_mode: no
|
||||
|
||||
- copy:
|
||||
dest: /var/www/html/reports/.htaccess
|
||||
content: Options +Indexes
|
||||
check_mode: no
|
||||
|
||||
- service:
|
||||
name: httpd
|
||||
state: started
|
||||
check_mode: no
|
||||
|
||||
- include_role:
|
||||
name: demo.patching.report_linux
|
||||
|
||||
- include_role:
|
||||
name: demo.patching.report_linux_patching
|
||||
delegate_to: "{{ report_server }}"
|
||||
run_once: yes
|
||||
@@ -134,6 +134,8 @@ controller_templates:
|
||||
playbook: "linux/patching.yml"
|
||||
execution_environment: Default execution environment
|
||||
use_fact_cache: true
|
||||
job_type: check
|
||||
ask_job_type_on_launch: yes
|
||||
credentials:
|
||||
- "Workshop Credential"
|
||||
survey_enabled: true
|
||||
|
||||