126 lines
4.7 KiB
Python
126 lines
4.7 KiB
Python
# Copyright 2015 Rackspace
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
import platform
|
|
import re
|
|
import subprocess
|
|
|
|
|
|
class PingClient(object):
|
|
"""
|
|
@summary: Client to ping windows or linux servers
|
|
"""
|
|
|
|
DEFAULT_NUM_PINGS = 3
|
|
PING_IPV4_COMMAND_LINUX = 'ping -c {num_pings}'
|
|
PING_IPV6_COMMAND_LINUX = 'ping6 -c {num_pings}'
|
|
PING_IPV4_COMMAND_WINDOWS = 'ping -c {num_pings}'
|
|
PING_IPV6_COMMAND_WINDOWS = 'ping -6 -c {num_pings}'
|
|
PING_PACKET_LOSS_REGEX = '(\d{1,3})\.?\d*\%.*loss'
|
|
|
|
@classmethod
|
|
def ping(cls, ip, ip_address_version, num_pings=DEFAULT_NUM_PINGS):
|
|
"""
|
|
@summary: Ping an IP address, return if replies were received or not.
|
|
@param ip: IP address to ping
|
|
@type ip: string
|
|
@param ip_address_version: IP Address version (4 or 6)
|
|
@type ip_address_version: int
|
|
@param num_pings: Number of pings to attempt
|
|
@type num_pings: int
|
|
@return: True if the server was reachable, False otherwise
|
|
@rtype: bool
|
|
"""
|
|
packet_loss_percent = cls._ping(
|
|
ip=ip, ip_address_version=ip_address_version, num_pings=num_pings)
|
|
return packet_loss_percent != '100'
|
|
|
|
@classmethod
|
|
def ping_percent_success(cls, ip, ip_address_version,
|
|
num_pings=DEFAULT_NUM_PINGS):
|
|
"""
|
|
@summary: Ping an IP address, return the percent of replies received
|
|
@param ip: IP address to ping
|
|
@type ip: string
|
|
@param ip_address_version: IP Address version (4 or 6)
|
|
@type ip_address_version: int
|
|
@param num_pings: Number of pings to attempt
|
|
@type num_pings: int
|
|
@return: Percent of responses received, based on number of requests
|
|
@rtype: int
|
|
"""
|
|
packet_loss_percent = cls._ping(
|
|
ip=ip, ip_address_version=ip_address_version, num_pings=num_pings)
|
|
return 100 - int(packet_loss_percent)
|
|
|
|
@classmethod
|
|
def ping_percent_loss(cls, ip, ip_address_version,
|
|
num_pings=DEFAULT_NUM_PINGS):
|
|
"""
|
|
@summary: Ping an IP address, return the percent of replies not
|
|
returned
|
|
@param ip: IP address to ping
|
|
@type ip: string
|
|
@param ip_address_version: IP Address version (4 or 6)
|
|
@type ip_address_version: int
|
|
@param num_pings: Number of pings to attempt
|
|
@type num_pings: int
|
|
@return: Percent of responses not received, based on number of requests
|
|
@rtype: int
|
|
"""
|
|
return int(cls._ping(
|
|
ip=ip, ip_address_version=ip_address_version, num_pings=num_pings))
|
|
|
|
@classmethod
|
|
def _ping(cls, ip, ip_address_version, num_pings):
|
|
"""
|
|
@summary: Ping an IP address
|
|
@param ip: IP address to ping
|
|
@type ip: string
|
|
@param ip_address_version: IP Address version (4 or 6)
|
|
@type ip_address_version: int
|
|
@param num_pings: Number of pings to attempt
|
|
@type num_pings: int
|
|
@return: Percent of ping replies received
|
|
@rtype: int
|
|
"""
|
|
|
|
windows = 'windows'
|
|
|
|
os_type = platform.system().lower()
|
|
ping_ipv4 = (cls.PING_IPV4_COMMAND_WINDOWS if windows in os_type
|
|
else cls.PING_IPV4_COMMAND_LINUX)
|
|
ping_ipv6 = (cls.PING_IPV6_COMMAND_WINDOWS if windows in os_type
|
|
else cls.PING_IPV6_COMMAND_LINUX)
|
|
|
|
ping_cmd = ping_ipv6 if ip_address_version == 6 else ping_ipv4
|
|
ping_cmd = ping_cmd.format(num_pings=num_pings)
|
|
cmd = '{command} {address}'.format(command=ping_cmd, address=ip)
|
|
|
|
process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE)
|
|
process.wait()
|
|
|
|
try:
|
|
packet_loss_percent = re.search(
|
|
cls.PING_PACKET_LOSS_REGEX,
|
|
process.stdout.read()).group(1)
|
|
except Exception:
|
|
# When there is no match, 100% ping loss is the best response
|
|
# (for now). There has to be a better way, since the regex not
|
|
# matching does not guarantee that the target IP is not online.
|
|
# e.g. - The ping utility was not available/located in the path or
|
|
# the expected ping output changed.
|
|
packet_loss_percent = 100
|
|
|
|
return packet_loss_percent
|