Skip to content

Commit

Permalink
Add satellite 5 support. Closes #522 #523 #520.
Browse files Browse the repository at this point in the history
  • Loading branch information
chambridge committed Feb 15, 2018
1 parent 9be1c91 commit 419d4f9
Show file tree
Hide file tree
Showing 10 changed files with 798 additions and 43 deletions.
20 changes: 14 additions & 6 deletions quipucords/scanner/satellite/connect.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"""ScanTask used for satellite connection task."""
from requests import exceptions
from django.db import transaction
from api.models import (ScanTask, TaskConnectionResult, SourceOptions)
from api.models import (ScanTask, TaskConnectionResult)
from scanner.task import ScanTaskRunner
from scanner.satellite import utils
from scanner.satellite.api import SatelliteException
Expand Down Expand Up @@ -51,22 +51,22 @@ def __init__(self, scan_job, scan_task):
# partial results and start over.
conn_result.systems.all().delete()

# pylint: disable=too-many-return-statements
def run(self):
"""Scan network range ang attempt connections."""
satellite_version = None
options = self.source.options
if options:
satellite_version = options.satellite_version

if (satellite_version is None or
satellite_version == SourceOptions.SATELLITE_VERSION_5):
error_message = 'Satellite version %s is not yet supported.\n' %\
SourceOptions.SATELLITE_VERSION_5
if satellite_version is None:
error_message = 'Satellite version is unknown. '
error_message += 'Connect scan failed for %s.' % self.scan_task
return error_message, ScanTask.FAILED

try:
status_code, api_version = utils.status(self.scan_task)
status_code, api_version = utils.status(self.scan_task,
satellite_version)
if status_code == 200:
api = create(satellite_version, api_version,
self.scan_task, self.conn_result)
Expand All @@ -90,5 +90,13 @@ def run(self):
error_message = 'Satellite error encountered: %s\n' % conn_error
error_message += 'Connect scan failed for %s.' % self.scan_task
return error_message, ScanTask.FAILED
except TimeoutError as timeout_error:
error_message = 'Satellite error encountered: %s\n' % timeout_error
error_message += 'Connect scan failed for %s.' % self.scan_task
return error_message, ScanTask.FAILED
except Exception as unknown_error: # pylint: disable=broad-except
error_message = 'Satellite error encountered: %s\n' % unknown_error
error_message += 'Inspect scan failed for %s.' % self.scan_task
return error_message, ScanTask.FAILED

return None, ScanTask.COMPLETED
6 changes: 4 additions & 2 deletions quipucords/scanner/satellite/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,16 @@
"""Factory for Satellite Interface."""
from api.models import SourceOptions
from scanner.satellite.six import (SatelliteSixV1, SatelliteSixV2)
from scanner.satellite.five import SatelliteFive


def create(satellite_version, api_version, scan_task,
conn_result, inspect_result=None):
"""Create the appropriate SatelliteInterface."""
if (satellite_version is None or
satellite_version == SourceOptions.SATELLITE_VERSION_5):
if satellite_version is None:
return None
if satellite_version == SourceOptions.SATELLITE_VERSION_5:
return SatelliteFive(scan_task, conn_result, inspect_result)
if api_version == 1:
return SatelliteSixV1(scan_task, conn_result, inspect_result)
if api_version == 2:
Expand Down
273 changes: 273 additions & 0 deletions quipucords/scanner/satellite/five.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,273 @@
#
# Copyright (c) 2018 Red Hat, Inc.
#
# This software is licensed to you under the GNU General Public License,
# version 3 (GPLv3). There is NO WARRANTY for this software, express or
# implied, including the implied warranties of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv3
# along with this software; if not, see
# https://www.gnu.org/licenses/gpl-3.0.txt.
#
"""Satellite 5 API handlers."""

import logging
import xmlrpc.client
from scanner.satellite.api import SatelliteInterface, SatelliteException
from scanner.satellite import utils


# Get an instance of a logger
logger = logging.getLogger(__name__) # pylint: disable=invalid-name

UNKNOWN = 'unknown'
ID = 'id'
NAME = 'name'
INTERFACE = 'interface'
ETHERNET = 'eth'
IP = 'ip'
HARDWARE_ADDRESS = 'hardware_address'
IP_ADDRESSES = 'ip_addresses'
MAC_ADDRESSES = 'mac_addresses'
UUID = 'uuid'
HOSTNAME = 'hostname'
REGISTRATION_TIME = 'registration_time'
LAST_CHECKIN = 'last_checkin'
LAST_CHECKIN_TIME = 'last_checkin_time'
RELEASE = 'release'
OS_NAME = 'os_name'
OS_VERSION = 'os_version'
OS_RELEASE = 'os_release'
IS_VIRT = 'is_virtualized'
KERNEL = 'kernel_version'
ARCH = 'arch'
ARCHITECTURE = 'architecture'
COUNT = 'count'
SOCKET_COUNT = 'socket_count'
CORES = 'cores'
SOCKETS = 'num_sockets'
ENTITLEMENTS = 'entitlements'
NUM_VIRTUAL_GUESTS = 'num_virtual_guests'
VIRTUAL = 'virtual'
VIRTUAL_HOST = 'virtual_host'
VIRTUAL_HOST_NAME = 'virtual_host_name'
HYPERVISOR = 'hypervisor'


class SatelliteFive(SatelliteInterface):
"""Interact with Satellite 5."""

def host_count(self):
"""Obtain the count of managed hosts."""
systems_count = 0
client, user, password = utils.get_sat5_client(self.scan_task)
try:
key = client.auth.login(user, password)
systems = client.system.list_user_systems(key)
systems_count = len(systems)
client.auth.logout(key)
except xmlrpc.client.Fault as xml_error:
raise SatelliteException(str(xml_error))
self.initialize_stats(systems_count)
return systems_count

def hosts(self):
"""Obtain the managed hosts."""
hosts = []
credential = utils.get_credential(self.scan_task)
client, user, password = utils.get_sat5_client(self.scan_task)
try:
key = client.auth.login(user, password)
systems = client.system.list_user_systems(key)
client.auth.logout(key)

for system in systems:
name = system.get(NAME)
if name is not None:
hosts.append(name)
self.record_conn_result(name, credential)

except xmlrpc.client.Fault as xml_error:
raise SatelliteException(str(xml_error))

return hosts

# pylint: disable=too-many-arguments, too-many-locals, too-many-statements
def host_details(self, host_id, host_name, last_checkin,
virtual_hosts, virtual_guests):
"""Obtain the details for a given host id and name.
:param host_id: The identifier of the host
:param host_name: The name of the host
:param last_checkin: The date of last checkin
:param virtual_hosts: A dictionary of virtual host data
:param virtual_guests: A dictionary of guest to host data
:returns: dictionary of host details
"""
details = {}
sys_result = self.inspect_result.systems.filter(
name=host_name).first()

if sys_result:
logger.debug('Results already captured for host_name=%s',
host_name)
return details
client, user, password = utils.get_sat5_client(self.scan_task)
try:
key = client.auth.login(user, password)
uuid = client.system.get_uuid(key, host_id)
if uuid is None or uuid == '':
uuid = host_id

cpu = client.system.get_cpu(key, host_id)
arch = cpu.get(ARCH, UNKNOWN)
cpu_count = cpu.get(COUNT)
cpu_core_count = cpu_count
cpu_socket_count = cpu.get(SOCKET_COUNT)

system_details = client.system.get_details(key, host_id)
hostname = system_details.get(HOSTNAME)
release = system_details.get(RELEASE)

kernel = client.system.get_running_kernel(key, host_id)

entitlements = []
subs = client.system.get_entitlements(key, host_id)
for sub in subs:
entitlement = {NAME: sub}
entitlements.append(entitlement)

network_devices = client.system.get_network_devices(key, host_id)
ip_addresses = []
mac_addresses = []
for device in network_devices:
interface = device.get(INTERFACE)
if interface and interface.startswith(ETHERNET):
ip_addr = device.get(IP)
if ip_addr:
ip_addresses.append(ip_addr)
mac = device.get(HARDWARE_ADDRESS)
if mac:
mac_addresses.append(mac)
registration_date = client.system.get_registration_date(key,
host_id)

details[UUID] = uuid
details[NAME] = host_name
details[HOSTNAME] = hostname
details[LAST_CHECKIN_TIME] = last_checkin
details[REGISTRATION_TIME] = str(registration_date)
details[ARCHITECTURE] = arch
details[KERNEL] = kernel
details[CORES] = cpu_core_count
details[SOCKETS] = cpu_socket_count
details[OS_RELEASE] = release
details[ENTITLEMENTS] = entitlements
details[IP_ADDRESSES] = ip_addresses
details[MAC_ADDRESSES] = mac_addresses

if virtual_hosts.get(host_id):
virtual_host = virtual_hosts.get(host_id)
details[VIRTUAL] = HYPERVISOR
guests = virtual_host.get(NUM_VIRTUAL_GUESTS, 0)
details[NUM_VIRTUAL_GUESTS] = guests
details[IS_VIRT] = False

elif virtual_guests.get(host_id):
virt_host_id = virtual_guests.get(host_id)
virtual_host = virtual_hosts.get(virt_host_id)
details[IS_VIRT] = True
if virtual_host.get(UUID):
details[VIRTUAL_HOST] = virtual_host.get(UUID)
if virtual_host.get(NAME):
details[VIRTUAL_HOST_NAME] = virtual_host.get(NAME)

client.auth.logout(key)
except xmlrpc.client.Fault as xml_error:
raise SatelliteException(str(xml_error))

self.record_inspect_result(host_name, details)
logger.debug('host_id=%s, host_details=%s',
host_id, details)
return details

def virtual_guests(self, virtual_host_id):
"""Obtain the virtual guest information for a virtual host.
:param virtual_host_id: The identifier for a virtual host
:returns: a tule of a dictionary of virtual guest id, virtual_host_id
and the number of guests
"""
virtual_guests = {}
virt_guests = []
client, user, password = utils.get_sat5_client(self.scan_task)
try:
key = client.auth.login(user, password)
virt_guests = client.system.list_virtual_guests(key,
virtual_host_id)
client.auth.logout(key)
except xmlrpc.client.Fault as xml_error:
raise SatelliteException(str(xml_error))

for guest in virt_guests:
virt_id = guest.get(ID)
virtual_guests[virt_id] = virtual_host_id

return (virtual_guests, len(virt_guests))

# pylint: disable=too-many-locals
def virtual_hosts(self):
"""Obtain the virtual host data.
:returns: tuple of (list of virtual host ids,
dictionary of virtual guest id to virtual_host_id)
"""
virt_hosts = []
virtual_hosts = {}
virtual_guests = {}
client, user, password = utils.get_sat5_client(self.scan_task)
try:
key = client.auth.login(user, password)
virt_hosts = client.system.list_virtual_hosts(key)
for virt_host in virt_hosts:
virt_host_id = virt_host.get(ID)
virt_host_name = virt_host.get(NAME)
uuid = client.system.get_uuid(key, virt_host_id)
if uuid is None or uuid == '':
uuid = virt_host_id
virtual_host = {ID: virt_host_id,
NAME: virt_host_name,
UUID: uuid}
virtual_hosts[virt_host_id] = virtual_host
client.auth.logout(key)
except xmlrpc.client.Fault as xml_error:
raise SatelliteException(str(xml_error))

for virt_host in virt_hosts:
virt_host_id = virt_host.get(ID)
virtual_host = virtual_hosts.get(virt_host_id)
guests, guest_count = self.virtual_guests(virt_host_id)
virtual_guests.update(guests)
virtual_host[NUM_VIRTUAL_GUESTS] = guest_count

return(virtual_hosts, virtual_guests)

def hosts_facts(self):
"""Obtain the managed hosts detail raw facts."""
systems_count = len(self.conn_result.systems.all())
self.initialize_stats(systems_count)

hosts = []
client, user, password = utils.get_sat5_client(self.scan_task)
try:
key = client.auth.login(user, password)
hosts = client.system.list_user_systems(key)
client.auth.logout(key)
except xmlrpc.client.Fault as xml_error:
raise SatelliteException(str(xml_error))

virtual_hosts, virtual_guests = self.virtual_hosts()

for host in hosts:
last_checkin = str(host.get(LAST_CHECKIN, ''))
self.host_details(host.get(ID), host.get(NAME), last_checkin,
virtual_hosts, virtual_guests)
18 changes: 10 additions & 8 deletions quipucords/scanner/satellite/inspect.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@
#
"""ScanTask used for satellite inspection task."""
from requests import exceptions
from api.models import (ScanTask, SourceOptions,
TaskInspectionResult)
from api.models import (ScanTask, TaskInspectionResult)
from scanner.task import ScanTaskRunner
from scanner.satellite import utils
from scanner.satellite.api import SatelliteException
Expand Down Expand Up @@ -53,15 +52,14 @@ def run(self):
if options:
satellite_version = options.satellite_version

if (satellite_version is None or
satellite_version == SourceOptions.SATELLITE_VERSION_5):
error_message = 'Satellite version %s is not yet supported.\n' %\
(SourceOptions.SATELLITE_VERSION_5)
error_message += 'Inspect scan failed for %s.' % self.scan_task
if satellite_version is None:
error_message = 'Satellite version is unknown. '
error_message += 'Connect scan failed for %s.' % self.scan_task
return error_message, ScanTask.FAILED

try:
status_code, api_version = utils.status(self.scan_task)
status_code, api_version = utils.status(self.scan_task,
satellite_version)
if status_code == 200:
self.conn_result = self.scan_job.connection_results.results.\
filter(scan_task=self.connect_scan_task.id).first()
Expand Down Expand Up @@ -102,6 +100,10 @@ def run(self):
error_message = 'Satellite error encountered: %s\n' % conn_error
error_message += 'Inspect scan failed for %s.' % self.scan_task
return error_message, ScanTask.FAILED
except TimeoutError as timeout_error:
error_message = 'Satellite error encountered: %s\n' % timeout_error
error_message += 'Connect scan failed for %s.' % self.scan_task
return error_message, ScanTask.FAILED
except Exception as unknown_error: # pylint: disable=broad-except
error_message = 'Satellite error encountered: %s\n' % unknown_error
error_message += 'Inspect scan failed for %s.' % self.scan_task
Expand Down
Loading

0 comments on commit 419d4f9

Please sign in to comment.