summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFeng Pan <fpan@redhat.com>2016-04-22 18:49:07 -0400
committerFeng Pan <fpan@redhat.com>2016-05-04 23:48:00 -0400
commit568656d2bd0341aacc74a936e28315b06f1881ca (patch)
tree5e9b45102935b03f2676be7fe5396828bc05729b
parentd93d2992ff5de7f60c47fd3c31e429e5c06f6bce (diff)
Add python parsing library for network settings file.
Changes: - Implements network_settings.yaml file parsing in python. - Adds support for both IPv4 and IPv6 in network_settings.yaml - Adds support for api_network in network_settings.yaml - Removes bash library functions for network related functions. - Adds dependency to python34-yaml for apex-common package. Note that support for ipv6 and api_network is not complete yet. Proper configuriration of network environment and nic template files will be added later. Change-Id: I087f725dabedfef109c9de1f58ce2611da647e87 Signed-off-by: Feng Pan <fpan@redhat.com>
-rw-r--r--build/opnfv-apex-common.spec12
-rwxr-xr-xci/deploy.sh127
-rw-r--r--lib/common-functions.sh320
-rwxr-xr-xlib/python/apex-python-utils.py67
-rw-r--r--lib/python/apex/__init__.py2
-rw-r--r--lib/python/apex/ip_utils.py228
-rw-r--r--lib/python/apex/net_env.py242
7 files changed, 542 insertions, 456 deletions
diff --git a/build/opnfv-apex-common.spec b/build/opnfv-apex-common.spec
index 89fb4030..56f0f305 100644
--- a/build/opnfv-apex-common.spec
+++ b/build/opnfv-apex-common.spec
@@ -11,7 +11,7 @@ Source0: opnfv-apex-common.tar.gz
BuildArch: noarch
BuildRequires: python-docutils python34-devel
Requires: openstack-tripleo opnfv-apex-sdn opnfv-apex-undercloud openvswitch qemu-kvm bridge-utils libguestfs-tools
-Requires: initscripts net-tools iputils iproute iptables python34
+Requires: initscripts net-tools iputils iproute iptables python34 python34-yaml
%description
Scripts for OPNFV deployment using RDO Manager
@@ -39,12 +39,14 @@ install config/deploy/os-onos-nofeature-ha.yaml %{buildroot}%{_sysconfdir}/opnfv
install config/deploy/os-opencontrail-nofeature-ha.yaml %{buildroot}%{_sysconfdir}/opnfv-apex/os-opencontrail-nofeature-ha.yaml
install config/network/network_settings.yaml %{buildroot}%{_sysconfdir}/opnfv-apex/network_settings.yaml
-mkdir -p %{buildroot}%{_var}/opt/opnfv/lib/
+mkdir -p %{buildroot}%{_var}/opt/opnfv/lib/python/apex
install lib/common-functions.sh %{buildroot}%{_var}/opt/opnfv/lib/
install lib/utility-functions.sh %{buildroot}%{_var}/opt/opnfv/lib/
+install lib/python/apex-python-utils.py %{buildroot}%{_var}/opt/opnfv/lib/python/
mkdir -p %{buildroot}%{python3_sitelib}/apex/
install lib/python/apex/__init__.py %{buildroot}%{python3_sitelib}/apex/
install lib/python/apex/ip_utils.py %{buildroot}%{python3_sitelib}/apex/
+install lib/python/apex/net_env.py %{buildroot}%{python3_sitelib}/apex/
mkdir -p %{buildroot}%{_var}/opt/opnfv/lib/installer/onos/
install lib/installer/onos/onos_gw_mac_update.sh %{buildroot}%{_var}/opt/opnfv/lib/installer/onos/
@@ -64,7 +66,11 @@ install config/inventory/pod_example_settings.yaml %{buildroot}%{_docdir}/opnfv/
%attr(755,root,root) %{_bindir}/opnfv-util
%{_var}/opt/opnfv/lib/common-functions.sh
%{_var}/opt/opnfv/lib/utility-functions.sh
+%{_var}/opt/opnfv/lib/python/apex-python-utils.py
+%{_var}/opt/opnfv/lib/python/apex-python-utils.pyo
+%{_var}/opt/opnfv/lib/python/apex-python-utils.pyc
%{python3_sitelib}/apex/ip_utils.py
+%{python3_sitelib}/apex/net_env.py
%{python3_sitelib}/apex/__init__.py
%{python3_sitelib}/apex/__pycache__/*
%{_var}/opt/opnfv/lib/installer/onos/onos_gw_mac_update.sh
@@ -84,6 +90,8 @@ install config/inventory/pod_example_settings.yaml %{buildroot}%{_docdir}/opnfv/
%doc %{_docdir}/opnfv/inventory.yaml.example
%changelog
+* Fri Apr 22 2016 Feng Pan <fpan@redhat.com> - 3.0-3
+- Adds python network setting parsing lib.
* Fri Apr 15 2016 Feng Pan <fpan@redhat.com> - 3.0-2
- Adds python ip utility lib.
* Mon Apr 11 2016 Tim Rozet <trozet@redhat.com> - 3.0-1
diff --git a/ci/deploy.sh b/ci/deploy.sh
index ebcde335..cd76558c 100755
--- a/ci/deploy.sh
+++ b/ci/deploy.sh
@@ -101,126 +101,13 @@ parse_setting_value() {
}
##parses network settings yaml into globals
parse_network_settings() {
- local required_network_settings="cidr"
- local common_optional_network_settings="usable_ip_range"
- local admin_network_optional_settings="provisioner_ip dhcp_range introspection_range"
- local public_network_optional_settings="floating_ip_range gateway provisioner_ip"
- local nic_value cidr
-
- eval $(parse_yaml ${NETSETS})
- for network in ${OPNFV_NETWORK_TYPES}; do
- if [[ $(eval echo \${${network}_enabled}) == 'true' ]]; then
- enabled_network_list+="${network} "
- elif [ "${network}" == 'admin_network' ]; then
- echo -e "${red}ERROR: You must enable admin_network and configure it explicitly or use auto-detection${reset}"
- exit 1
- elif [[ "${network}" == 'public_network' && "$net_isolation_enabled" == "TRUE" ]]; then
- echo -e "${red}ERROR: You must enable public_network and configure it explicitly or use auto-detection${reset}"
+ if output=$(python3.4 -B $CONFIG/lib/python/apex-python-utils.py parse_net_settings -n $NETSETS -i $net_isolation_enabled); then
+ eval "$output"
+ echo -e "${blue}${output}${reset}"
+ else
exit 1
- else
- echo -e "${blue}INFO: Network: ${network} is disabled, will collapse into admin_network"
- fi
- done
+ fi
- # check for enabled network values
- for enabled_network in ${enabled_network_list}; do
- # detect required settings first to continue
- echo -e "${blue}INFO: Detecting Required settings for: ${enabled_network}${reset}"
- for setting in ${required_network_settings}; do
- eval "setting_value=\${${enabled_network}_${setting}}"
- if [ -z "${setting_value}" ]; then
- # if setting is missing we try to autodetect
- eval "nic_value=\${${enabled_network}_bridged_interface}"
- if [ -n "$nic_value" ]; then
- setting_value=$(eval find_${setting} ${nic_value})
- if [ -n "$setting_value" ]; then
- eval "${enabled_network}_${setting}=${setting_value}"
- echo -e "${blue}INFO: Auto-detection: ${enabled_network}_${setting}: ${setting_value}${reset}"
- else
- echo -e "${red}ERROR: Auto-detection failed: ${setting} not found using interface: ${nic_value}${reset}"
- exit 1
- fi
- else
- echo -e "${red}ERROR: Required setting: ${setting} not found, and bridge interface not provided\
-for Auto-detection${reset}"
- exit 1
- fi
- else
- echo -e "${blue}INFO: ${enabled_network}_${setting}: ${setting_value}${reset}"
- fi
- done
- echo -e "${blue}INFO: Detecting Common settings for: ${enabled_network}${reset}"
- # detect optional common settings
- # these settings can be auto-generated if missing
- for setting in ${common_optional_network_settings}; do
- eval "setting_value=\${${enabled_network}_${setting}}"
- if [ -z "${setting_value}" ]; then
- if [ -n "$nic_value" ]; then
- setting_value=$(eval find_${setting} ${nic_value})
- else
- setting_value=''
- echo -e "${blue}INFO: Skipping Auto-detection, NIC not specified for ${enabled_network}. Attempting Auto-generation...${reset}"
- fi
- if [ -n "$setting_value" ]; then
- eval "${enabled_network}_${setting}=${setting_value}"
- echo -e "${blue}INFO: Auto-detection: ${enabled_network}_${setting}: ${setting_value}${reset}"
- else
- # if Auto-detection fails we can auto-generate with CIDR
- eval "cidr=\${${enabled_network}_cidr}"
- if [ -n "$cidr" ]; then
- echo -e "${blue}INFO: Auto-generating: ${setting}${reset}"
- setting_value=$(eval generate_${setting} ${cidr})
- else
- setting_value=''
- echo -e "${red}ERROR: Auto-generation failed: required parameter CIDR missing for network ${enabled_network}${reset}"
- fi
- if [ -n "$setting_value" ]; then
- eval "${enabled_network}_${setting}=${setting_value}"
- echo -e "${blue}INFO: Auto-generated: ${enabled_network}_${setting}: ${setting_value}${reset}"
- else
- echo -e "${red}ERROR: Auto-generation failed: ${setting} not found${reset}"
- exit 1
- fi
- fi
- else
- echo -e "${blue}INFO: ${enabled_network}_${setting}: ${setting_value}${reset}"
- fi
- done
- echo -e "${blue}INFO: Detecting Network Specific settings for: ${enabled_network}${reset}"
- # detect network specific settings
- for setting in $(eval echo \${${enabled_network}_optional_settings}); do
- eval "setting_value=\${${enabled_network}_${setting}}"
- if [ -z "${setting_value}" ]; then
- if [ -n "$nic_value" ]; then
- setting_value=$(eval find_${setting} ${nic_value})
- else
- setting_value=''
- echo -e "${blue}INFO: Skipping Auto-detection, NIC not specified for ${enabled_network}. Attempting Auto-generation...${reset}"
- fi
- if [ -n "$setting_value" ]; then
- eval "${enabled_network}_${setting}=${setting_value}"
- echo -e "${blue}INFO: Auto-detection: ${enabled_network}_${setting}: ${setting_value}${reset}"
- else
- eval "cidr=\${${enabled_network}_cidr}"
- if [ -n "$cidr" ]; then
- setting_value=$(eval generate_${setting} ${cidr})
- else
- setting_value=''
- echo -e "${red}ERROR: Auto-generation failed: required parameter CIDR missing for network ${enabled_network}${reset}"
- fi
- if [ -n "$setting_value" ]; then
- eval "${enabled_network}_${setting}=${setting_value}"
- echo -e "${blue}INFO: Auto-generated: ${enabled_network}_${setting}: ${setting_value}${reset}"
- else
- echo -e "${red}ERROR: Auto-generation failed: ${setting} not found${reset}"
- exit 1
- fi
- fi
- else
- echo -e "${blue}INFO: ${enabled_network}_${setting}: ${setting_value}${reset}"
- fi
- done
- done
}
##parses deploy settings yaml into globals and options array
##params: none
@@ -686,7 +573,7 @@ function configure_network_environment {
sed -i '/EC2MetadataIp/c\\ EC2MetadataIp: '${admin_network_provisioner_ip}'' $1
# check for private network
- if [[ ! -z "$private_network_enabled" && "$private_network_enabled" == "true" ]]; then
+ if [[ ! -z "$private_network_enabled" && "$private_network_enabled" == "True" ]]; then
sed -i 's#^.*Network::Tenant.*$# OS::TripleO::Network::Tenant: '${tht_dir}'/tenant.yaml#' $1
sed -i 's#^.*Controller::Ports::TenantPort:.*$# OS::TripleO::Controller::Ports::TenantPort: '${tht_dir}'/ports/tenant.yaml#' $1
sed -i 's#^.*Compute::Ports::TenantPort:.*$# OS::TripleO::Compute::Ports::TenantPort: '${tht_dir}'/ports/tenant.yaml#' $1
@@ -700,7 +587,7 @@ function configure_network_environment {
fi
# check for storage network
- if [[ ! -z "$storage_network_enabled" && "$storage_network_enabled" == "true" ]]; then
+ if [[ ! -z "$storage_network_enabled" && "$storage_network_enabled" == "True" ]]; then
sed -i 's#^.*Network::Storage:.*$# OS::TripleO::Network::Storage: '${tht_dir}'/storage.yaml#' $1
sed -i 's#^.*Network::Ports::StorageVipPort:.*$# OS::TripleO::Network::Ports::StorageVipPort: '${tht_dir}'/ports/storage.yaml#' $1
sed -i 's#^.*Controller::Ports::StoragePort:.*$# OS::TripleO::Controller::Ports::StoragePort: '${tht_dir}'/ports/storage.yaml#' $1
diff --git a/lib/common-functions.sh b/lib/common-functions.sh
index 32ee6bcc..e7041ac4 100644
--- a/lib/common-functions.sh
+++ b/lib/common-functions.sh
@@ -2,9 +2,6 @@
# Common Functions used by OPNFV Apex
# author: Tim Rozet (trozet@redhat.com)
-#python ip_gen command
-ip_gen="python3.4 -B -m apex.ip_utils generate_ip_range"
-
##converts subnet mask to prefix
##params: subnet mask
function prefix2mask {
@@ -17,324 +14,11 @@ function prefix2mask {
##find ip of interface
##params: interface name
function find_ip {
- ip addr show $1 | grep -Eo '^\s+inet\s+[\.0-9]+' | awk '{print $2}'
-}
-
-##finds subnet of ip and netmask
-##params: ip, netmask
-function find_subnet {
- IFS=. read -r i1 i2 i3 i4 <<< "$1"
- IFS=. read -r m1 m2 m3 m4 <<< "$2"
- printf "%d.%d.%d.%d\n" "$((i1 & m1))" "$((i2 & m2))" "$((i3 & m3))" "$((i4 & m4))"
-}
-
-##verify subnet has at least n IPs
-##params: subnet mask, n IPs
-function verify_subnet_size {
- IFS=. read -r i1 i2 i3 i4 <<< "$1"
- num_ips_required=$2
-
- ##this function assumes you would never need more than 254
- ##we check here to make sure
- if [ "$num_ips_required" -ge 254 ]; then
- echo -e "\n\n${red}ERROR: allocating more than 254 IPs is unsupported...Exiting${reset}\n\n"
- return 1
- fi
-
- ##we just return if 3rd octet is not 255
- ##because we know the subnet is big enough
- if [ "$i3" -ne 255 ]; then
- return 0
- elif [ $((254-$i4)) -ge "$num_ips_required" ]; then
- return 0
- else
- echo -e "\n\n${red}ERROR: Subnet is too small${reset}\n\n"
- return 1
- fi
-}
-
-##finds last usable ip (broadcast minus 1) of a subnet from an IP and netmask
-## Warning: This function only works for IPv4 at the moment.
-##params: ip, netmask
-function find_last_ip_subnet {
- IFS=. read -r i1 i2 i3 i4 <<< "$1"
- IFS=. read -r m1 m2 m3 m4 <<< "$2"
- IFS=. read -r s1 s2 s3 s4 <<< "$((i1 & m1)).$((i2 & m2)).$((i3 & m3)).$((i4 & m4))"
- printf "%d.%d.%d.%d\n" "$((255 - $m1 + $s1))" "$((255 - $m2 + $s2))" "$((255 - $m3 + $s3))" "$((255 - $m4 + $s4 - 1))"
-}
-
-##increments subnet by a value
-##params: ip, value
-##assumes low value
-function increment_subnet {
- IFS=. read -r i1 i2 i3 i4 <<< "$1"
- printf "%d.%d.%d.%d\n" "$i1" "$i2" "$i3" "$((i4 | $2))"
-}
-
-##finds netmask of interface
-##params: interface
-##returns long format 255.255.x.x
-function find_netmask {
- ifconfig $1 | grep -Eo 'netmask\s+[\.0-9]+' | awk '{print $2}'
-}
-
-##finds short netmask of interface
-##params: interface
-##returns short format, ex: /21
-function find_short_netmask {
- echo "/$(ip addr show $1 | grep -Eo '^\s+inet\s+[\/\.0-9]+' | awk '{print $2}' | cut -d / -f2)"
-}
-
-##increments next IP
-##params: ip
-##assumes a /24 subnet
-function next_ip {
- baseaddr="$(echo $1 | cut -d. -f1-3)"
- lsv="$(echo $1 | cut -d. -f4)"
- if [ "$lsv" -ge 254 ]; then
- return 1
- fi
- ((lsv++))
- echo $baseaddr.$lsv
-}
-
-##subtracts a value from an IP address
-##params: last ip, ip_count
-##assumes ip_count is less than the last octect of the address
-subtract_ip() {
- IFS=. read -r i1 i2 i3 i4 <<< "$1"
- ip_count=$2
- if [ $i4 -lt $ip_count ]; then
- echo -e "\n\n${red}ERROR: Can't subtract $ip_count from IP address $1 Exiting${reset}\n\n"
- exit 1
- fi
- printf "%d.%d.%d.%d\n" "$i1" "$i2" "$i3" "$((i4 - $ip_count ))"
-}
-
-##check if IP is in use
-##params: ip
-##ping ip to get arp entry, then check arp
-function is_ip_used {
- ping -c 5 $1 > /dev/null 2>&1
- arp -n | grep "$1 " | grep -iv incomplete > /dev/null 2>&1
-}
-
-##find next usable IP
-##params: ip
-function next_usable_ip {
- new_ip=$(next_ip $1)
- while [ "$new_ip" ]; do
- if ! is_ip_used $new_ip; then
- echo $new_ip
- return 0
- fi
- new_ip=$(next_ip $new_ip)
- done
- return 1
-}
-
-##increment ip by value
-##params: ip, amount to increment by
-##increment_ip $next_private_ip 10
-function increment_ip {
- baseaddr="$(echo $1 | cut -d. -f1-3)"
- lsv="$(echo $1 | cut -d. -f4)"
- incrval=$2
- lsv=$((lsv+incrval))
- if [ "$lsv" -ge 254 ]; then
- return 1
- fi
- echo $baseaddr.$lsv
-}
-
-##finds gateway on system
-##params: interface to validate gateway on (optional)
-##find_gateway em1
-function find_gateway {
- local gw gw_interface
- if [ -z "$1" ]; then
- return 1
- fi
- gw=$(ip route | grep default | awk '{print $3}')
- gw_interface=$(ip route get $gw | awk '{print $3}')
- if [ -n "$1" ]; then
- if [ "$gw_interface" == "$1" ]; then
- echo ${gw}
- fi
- fi
-}
-
-##finds subnet in CIDR notation for interface
-##params: interface to find CIDR
-function find_cidr {
- local cidr network ip netmask short_mask
- if [ -z "$1" ]; then
- return 1
- fi
- ip=$(find_ip $1)
- netmask=$(find_netmask $1)
- if [[ -z "$ip" || -z "$netmask" ]]; then
- return 1
- fi
- network=$(find_subnet ${ip} ${netamsk})
- short_mask=$(find_short_netmask $1)
- if [[ -z "$network" || -z "$short_mask" ]]; then
- return 1
- fi
- cidr="${subnet}'\'${short_mask}"
- echo ${cidr}
-}
-
-##finds block of usable IP addresses for an interface
-##simply returns at the moment the correct format
-##after first 20 IPs, and leave 20 IPs at end of subnet (for floating ips, etc)
-##params: interface to find IP
-function find_usable_ip_range {
- local interface_ip subnet_mask first_block_ip last_block_ip
- if [ -z "$1" ]; then
- return 1
- fi
- interface_ip=$(find_ip $1)
- subnet_mask=$(find_netmask $1)
- if [[ -z "$interface_ip" || -z "$subnet_mask" ]]; then
- return 1
- fi
- interface_ip=$(increment_ip ${interface_ip} 20)
- first_block_ip=$(next_usable_ip ${interface_ip})
- if [ -z "$first_block_ip" ]; then
- return 1
- fi
- last_block_ip=$(find_last_ip_subnet ${interface_ip} ${subnet_mask})
- if [ -z "$last_block_ip" ]; then
- return 1
- else
- last_block_ip=$(subtract_ip ${last_block_ip} 21)
- echo "${first_block_ip},${last_block_ip}"
- fi
-
-}
-
-##generates usable IP range in correct format based on CIDR
-##A block of 20 IP addresses are reserved at beginning of address space.
-##A block of 22 IP addresses are reserved at end of address space, this includes
-##the broadcast IP address.
-##In a /24 IPv4 CIDR, this results in .1-20 as as .234-255 being excluded.
-##params: cidr
-function generate_usable_ip_range {
- if [ -z "$1" ]; then
- return 1
- fi
- echo $($ip_gen $1 21 -23)
-}
-
-
-##find the undercloud IP address
-##finds first usable IP on subnet
-##params: interface
-function find_provisioner_ip {
- local interface_ip
- if [ -z "$1" ]; then
- return 1
- fi
- interface_ip=$(find_ip $1)
- if [ -z "$interface_ip" ]; then
- return 1
- fi
- echo $(increment_ip ${interface_ip} 1)
-}
-
-##generates undercloud IP address based on CIDR
-##params: cidr
-function generate_provisioner_ip {
- if [ -z "$1" ]; then
- return 1
- fi
- echo $($ip_gen $1 1 1)
-}
-
-
-##finds the dhcp range available via interface
-##uses first 8 IPs, after 2nd IP
-##params: interface
-function find_dhcp_range {
- local dhcp_range_start dhcp_range_end interface_ip
- if [ -z "$1" ]; then
- return 1
- fi
- interface_ip=$(find_ip $1)
- if [ -z "$interface_ip" ]; then
- return 1
- fi
- dhcp_range_start=$(increment_ip ${interface_ip} 2)
- dhcp_range_end=$(increment_ip ${dhcp_range_start} 8)
- echo "${dhcp_range_start},${dhcp_range_end}"
-}
-
-##generates the dhcp range available via CIDR
-##uses first 8 IPs, after 1st IP
-##params: cidr
-function generate_dhcp_range {
- if [ -z "$1" ]; then
- return 1
- fi
- echo $($ip_gen $1 2 10)
-}
-
-##finds the introspection range available via interface
-##uses 8 IPs, after the first 10 IPs
-##params: interface
-function find_introspection_range {
- local inspect_range_start inspect_range_end interface_ip
- if [ -z "$1" ]; then
- return 1
- fi
- interface_ip=$(find_ip $1)
- if [ -z "$interface_ip" ]; then
- return 1
- fi
- inspect_range_start=$(increment_ip ${interface_ip} 10)
- inspect_range_end=$(increment_ip ${inspect_range_start} 8)
- echo "${inspect_range_start},${inspect_range_end}"
-}
-
-##generate the introspection range available via CIDR
-##uses 8 IPs, after the first 10 IPs
-##params: cidr
-function generate_introspection_range {
- if [ -z "$1" ]; then
- return 1
- fi
- echo $($ip_gen $1 11 19)
-}
-
-##finds the floating ip range available via interface
-##uses last 20 IPs of a subnet, minus last IP
-##params: interface
-function find_floating_ip_range {
- local float_range_start float_range_end interface_ip subnet_mask
- if [ -z "$1" ]; then
- return 1
- fi
- interface_ip=$(find_ip $1)
- subnet_mask=$(find_netmask $1)
- if [[ -z "$interface_ip" || -z "$subnet_mask" ]]; then
+ if [[ -z "$1" ]]; then
return 1
fi
- float_range_end=$(find_last_ip_subnet ${interface_ip} ${subnet_mask})
- float_range_end=$(subtract_ip ${float_range_end} 1)
- float_range_start=$(subtract_ip ${float_range_end} 19)
- echo "${float_range_start},${float_range_end}"
-}
-##generate the floating range available via CIDR
-##uses last 20 IPs of subnet, minus last 2 IPs.
-##In a /24 IPv4 CIDR, this would result in floating ip range of .234-253
-##params: cidr
-function generate_floating_ip_range {
- if [ -z "$1" ]; then
- return 1
- fi
- echo $($ip_gen $1 -22 -3)
+ python3.4 -B $CONFIG/lib/python/apex-python-utils.py find_ip -i $1
}
##attach interface to OVS and set the network config correctly
diff --git a/lib/python/apex-python-utils.py b/lib/python/apex-python-utils.py
new file mode 100755
index 00000000..802e8571
--- /dev/null
+++ b/lib/python/apex-python-utils.py
@@ -0,0 +1,67 @@
+##############################################################################
+# Copyright (c) 2016 Feng Pan (fpan@redhat.com)
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+
+import argparse
+import sys
+import apex
+import logging
+import os
+
+def parse_net_settings(settings_args):
+ settings = apex.NetworkSettings(settings_args.path,
+ settings_args.network_isolation)
+ settings.dump_bash()
+
+
+def find_ip(int_args):
+ interface = apex.ip_utils.get_interface(int_args.interface,
+ int_args.address_family)
+ if interface:
+ print(interface.ip)
+
+
+parser = argparse.ArgumentParser()
+parser.add_argument('--DEBUG', action='store_true', default=False,
+ help="Turn on debug messages")
+subparsers = parser.add_subparsers()
+
+net_settings = subparsers.add_parser('parse_net_settings',
+ help='Parse network settings file')
+net_settings.add_argument('-n', '--path', default='network_settings.yaml',
+ help='path to network settings file')
+net_settings.add_argument('-i', '--network_isolation', type=bool, default=True,
+ help='network isolation')
+net_settings.set_defaults(func=parse_net_settings)
+
+get_int_ip = subparsers.add_parser('find_ip',
+ help='Find interface ip')
+get_int_ip.add_argument('-i', '--interface', required=True,
+ help='Interface name')
+get_int_ip.add_argument('-af', '--address_family', default=4, type=int,
+ choices=[4, 6],
+ help='IP Address family')
+get_int_ip.set_defaults(func=find_ip)
+
+args = parser.parse_args(sys.argv[1:])
+if args.DEBUG:
+ logging.basicConfig(level=logging.DEBUG)
+else:
+ apex_log_filename = '/var/log/apex/apex.log'
+ os.makedirs(os.path.dirname(apex_log_filename), exist_ok=True)
+ logging.basicConfig(filename=apex_log_filename,
+ format='%(asctime)s %(levelname)s: %(message)s',
+ datefmt='%m/%d/%Y %I:%M:%S %p',
+ level=logging.DEBUG)
+
+if hasattr(args, 'func'):
+ args.func(args)
+else:
+ parser.print_help()
+ exit(1)
diff --git a/lib/python/apex/__init__.py b/lib/python/apex/__init__.py
index 0c0ae6c6..88b066b2 100644
--- a/lib/python/apex/__init__.py
+++ b/lib/python/apex/__init__.py
@@ -7,3 +7,5 @@
# http://www.apache.org/licenses/LICENSE-2.0
##############################################################################
+
+from .net_env import NetworkSettings
diff --git a/lib/python/apex/ip_utils.py b/lib/python/apex/ip_utils.py
index 680ce7e0..d7099db5 100644
--- a/lib/python/apex/ip_utils.py
+++ b/lib/python/apex/ip_utils.py
@@ -1,4 +1,3 @@
-
##############################################################################
# Copyright (c) 2016 Feng Pan (fpan@redhat.com) and others.
#
@@ -10,6 +9,130 @@
import ipaddress
+import subprocess
+import re
+import logging
+
+
+def get_ip_range(start_offset=None, count=None, end_offset=None,
+ cidr=None, interface=None):
+ """
+ Generate IP range for a network (cidr) or an interface.
+
+ If CIDR is provided, it will take precedence over interface. In this case,
+ The entire CIDR IP address space is considered usable. start_offset will be
+ calculated from the network address, and end_offset will be calculated from
+ the last address in subnet.
+
+ If interface is provided, the interface IP will be used to calculate
+ offsets:
+ - If the interface IP is in the first half of the address space,
+ start_offset will be calculated from the interface IP, and end_offset
+ will be calculated from end of address space.
+ - If the interface IP is in the second half of the address space,
+ start_offset will be calculated from the network address in the address
+ space, and end_offset will be calculated from the interface IP.
+
+ 2 of start_offset, end_offset and count options must be provided:
+ - If start_offset and end_offset are provided, a range from start_offset
+ to end_offset will be returned.
+ - If count is provided, a range from either start_offset to (start_offset
+ +count) or (end_offset-count) to end_offset will be returned. The
+ IP range returned will be of size <count>.
+ Both start_offset and end_offset must be greater than 0.
+
+ Returns IP range in the format of "first_addr,second_addr" or exception
+ is raised.
+ """
+ if cidr:
+ if count and start_offset and not end_offset:
+ start_index = start_offset
+ end_index = start_offset + count -1
+ elif count and end_offset and not start_offset:
+ end_index = -1 - end_offset
+ start_index = -1 - end_index - count + 1
+ elif start_offset and end_offset and not count:
+ start_index = start_offset
+ end_index = -1 - end_offset
+ else:
+ raise IPUtilsException("Argument error: must pass in exactly 2 of"
+ "start_offset, end_offset and count")
+
+ start_ip = cidr[start_index]
+ end_ip = cidr[end_index]
+ network = cidr
+ elif interface:
+ network = interface.network
+ number_of_addr = network.num_addresses
+ if interface.ip < network[int(number_of_addr / 2)]:
+ if count and start_offset and not end_offset:
+ start_ip = interface.ip + start_offset
+ end_ip = start_ip + count - 1
+ elif count and end_offset and not start_offset:
+ end_ip = network[-1 - end_offset]
+ start_ip = end_ip - count + 1
+ elif start_offset and end_offset and not count:
+ start_ip = interface.ip + start_offset
+ end_ip = network[-1 - end_offset]
+ else:
+ raise IPUtilsException(
+ "Argument error: must pass in exactly 2 of"
+ "start_offset, end_offset and count")
+ else:
+ if count and start_offset and not end_offset:
+ start_ip = network[start_offset]
+ end_ip = start_ip + count -1
+ elif count and end_offset and not start_offset:
+ end_ip = interface.ip - end_offset
+ start_ip = end_ip - count + 1
+ elif start_offset and end_offset and not count:
+ start_ip = network[start_offset]
+ end_ip = interface.ip - end_offset
+ else:
+ raise IPUtilsException(
+ "Argument error: must pass in exactly 2 of"
+ "start_offset, end_offset and count")
+
+ else:
+ raise IPUtilsException("Must pass in cidr or interface to generate"
+ "ip range")
+
+ range_result = _validate_ip_range(start_ip, end_ip, network)
+ if range_result:
+ ip_range = "{},{}".format(start_ip, end_ip)
+ return ip_range
+ else:
+ raise IPUtilsException("Invalid IP range: {},{} for network {}"
+ .format(start_ip, end_ip, network))
+
+
+def get_ip(offset, cidr=None, interface=None):
+ """
+ Returns an IP in a network given an offset.
+
+ Either cidr or interface must be provided, cidr takes precedence.
+
+ If cidr is provided, offset is calculated from network address.
+ If interface is provided, offset is calculated from interface IP.
+
+ offset can be positive or negative, but the resulting IP address must also
+ be contained in the same subnet, otherwise an exception will be raised.
+
+ returns a IP address object.
+ """
+ if cidr:
+ ip = cidr[0 + offset]
+ network = cidr
+ elif interface:
+ ip = interface.ip + offset
+ network = interface.network
+ else:
+ raise IPUtilsException("Must pass in cidr or interface to generate IP")
+
+ if ip not in network:
+ raise IPUtilsException("IP {} not in network {}".format(ip, network))
+ else:
+ return str(ip)
def generate_ip_range(args):
@@ -22,7 +145,8 @@ def generate_ip_range(args):
start_position: starting index, default to first address in subnet (1)
end_position: ending index, default to last address in subnet (-1)
- Returns IP range in string format. A single IP is returned if start and end IPs are identical.
+ Returns IP range in string format. A single IP is returned if start and
+ end IPs are identical.
"""
cidr = ipaddress.ip_network(args.CIDR)
(start_index, end_index) = (args.start_position, args.end_position)
@@ -32,23 +156,95 @@ def generate_ip_range(args):
return ','.join(sorted([str(cidr[start_index]), str(cidr[end_index])]))
-def main():
- import argparse
- import sys
+def get_interface(nic, address_family=4):
+ """
+ Returns interface object for a given NIC name in the system
+
+ Only global address will be returned at the moment.
+
+ Returns interface object if an address is found for the given nic,
+ otherwise returns None.
+ """
+ if not nic.strip():
+ logging.error("empty nic name specified")
+ return None
+ output = subprocess.getoutput("ip -{} addr show {} scope global"
+ .format(address_family, nic))
+ if address_family == 4:
+ pattern = re.compile("\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/\d{1,2}")
+ elif address_family == 6:
+ pattern = re.compile("([0-9a-f]{0,4}:){2,7}[0-9a-f]{0,4}/\d{1,3}")
+ else:
+ raise IPUtilsException("Invalid address family: {}"
+ .format(address_family))
+ match = re.search(pattern, output)
+ if match:
+ logging.info("found interface {} ip: {}".format(nic, match.group()))
+ return ipaddress.ip_interface(match.group())
+ else:
+ logging.info("interface ip not found! ip address output:\n{}"
+ .format(output))
+ return None
+
+
+def find_gateway(interface):
+ """
+ Validate gateway on the system
+
+ Ensures that the provided interface object is in fact configured as default
+ route on the system.
+
+ Returns gateway IP (reachable from interface) if default route is found,
+ otherwise returns None.
+ """
+
+ address_family = interface.version
+ output = subprocess.getoutput("ip -{} route".format(address_family))
- parser = argparse.ArgumentParser()
- subparsers = parser.add_subparsers()
+ pattern = re.compile("default\s+via\s+(\S+)\s+")
+ match = re.search(pattern, output)
- parser_gen_ip_range = subparsers.add_parser('generate_ip_range', help='Generate IP Range given CIDR')
- parser_gen_ip_range.add_argument('CIDR', help='Network in CIDR notation')
- parser_gen_ip_range.add_argument('start_position', type=int, help='Starting index')
- parser_gen_ip_range.add_argument('end_position', type=int, help='Ending index')
- parser_gen_ip_range.set_defaults(func=generate_ip_range)
+ if match:
+ gateway_ip = match.group(1)
+ reverse_route_output = subprocess.getoutput("ip route get {}"
+ .format(gateway_ip))
+ pattern = re.compile("{}.+src\s+{}".format(gateway_ip, interface.ip))
+ if not re.search(pattern, reverse_route_output):
+ logging.warning("Default route doesn't match interface specified: "
+ "{}".format(reverse_route_output))
+ return None
+ else:
+ return gateway_ip
+ else:
+ logging.warning("Can't find gateway address on system")
+ return None
+
+
+def _validate_ip_range(start_ip, end_ip, cidr):
+ """
+ Validates an IP range is in good order and the range is part of cidr.
+
+ Returns True if validation succeeds, False otherwise.
+ """
+ ip_range = "{},{}".format(start_ip, end_ip)
+ if end_ip <= start_ip:
+ logging.warning("IP range {} is invalid: end_ip should be greater than "
+ "starting ip".format(ip_range))
+ return False
+ if start_ip not in ipaddress.ip_network(cidr):
+ logging.warning('start_ip {} is not in network {}'
+ .format(start_ip, cidr))
+ return False
+ if end_ip not in ipaddress.ip_network(cidr):
+ logging.warning('end_ip {} is not in network {}'.format(end_ip, cidr))
+ return False
- args = parser.parse_args(sys.argv[1:])
- print(args.func(args))
+ return True
-if __name__ == '__main__':
- main()
+class IPUtilsException(Exception):
+ def __init__(self, value):
+ self.value = value
+ def __str__(self):
+ return self.value
diff --git a/lib/python/apex/net_env.py b/lib/python/apex/net_env.py
new file mode 100644
index 00000000..ec46fe28
--- /dev/null
+++ b/lib/python/apex/net_env.py
@@ -0,0 +1,242 @@
+##############################################################################
+# Copyright (c) 2016 Feng Pan (fpan@redhat.com) and others.
+#
+# All rights reserved. This program and the accompanying materials
+# are made available under the terms of the Apache License, Version 2.0
+# which accompanies this distribution, and is available at
+# http://www.apache.org/licenses/LICENSE-2.0
+##############################################################################
+
+
+import yaml
+import logging
+import ipaddress
+from . import ip_utils
+
+
+ADMIN_NETWORK = 'admin_network'
+PRIVATE_NETWORK = 'private_network'
+PUBLIC_NETWORK = 'public_network'
+STORAGE_NETWORK = 'storage_network'
+API_NETWORK = 'api_network'
+OPNFV_NETWORK_TYPES = [ADMIN_NETWORK, PRIVATE_NETWORK, PUBLIC_NETWORK,
+ STORAGE_NETWORK, API_NETWORK]
+
+
+class NetworkSettings:
+ """
+ This class parses APEX network settings yaml file into an object. It
+ generates or detects all missing fields for deployment.
+
+ The resulting object will be used later to generate network environment file
+ as well as configuring post deployment networks.
+
+ Currently the parsed object is dumped into a bash global definition file
+ for deploy.sh consumption. This object will later be used directly as
+ deployment script move to python.
+ """
+ def __init__(self, filename, network_isolation):
+ with open(filename, 'r') as network_settings_file:
+ self.settings_obj = yaml.load(network_settings_file)
+ self.network_isolation = network_isolation
+ self.enabled_network_list = []
+ self._validate_input()
+
+ def _validate_input(self):
+ """
+ Validates the network settings file and populates all fields.
+
+ NetworkSettingsException will be raised if validation fails.
+ """
+ if ADMIN_NETWORK not in self.settings_obj or \
+ self.settings_obj[ADMIN_NETWORK].get('enabled') != True:
+ raise NetworkSettingsException("You must enable admin_network "
+ "and configure it explicitly or "
+ "use auto-detection")
+ if self.network_isolation and \
+ (PUBLIC_NETWORK not in self.settings_obj or
+ self.settings_obj[PUBLIC_NETWORK].get('enabled') != True):
+ raise NetworkSettingsException("You must enable public_network "
+ "and configure it explicitly or "
+ "use auto-detection")
+
+ for network in OPNFV_NETWORK_TYPES:
+ if network in self.settings_obj:
+ if self.settings_obj[network].get('enabled') == True:
+ logging.info("{} enabled".format(network))
+ self._config_required_settings(network)
+ self._config_ip_range(network=network,
+ setting='usable_ip_range',
+ start_offset=21, end_offset=21)
+ self._config_optional_settings(network)
+ self.enabled_network_list.append(network)
+ else:
+ logging.info("{} disabled, will collapse with "
+ "admin_network".format(network))
+ else:
+ logging.info("{} is not in specified, will collapse with "
+ "admin_network".format(network))
+
+ def _config_required_settings(self, network):
+ """
+ Configures either CIDR or bridged_interface setting
+
+ cidr takes precedence if both cidr and bridged_interface are specified
+ for a given network.
+
+ When using bridged_interface, we will detect network setting on the
+ given NIC in the system. The resulting config in settings object will
+ be an ipaddress.network object, replacing the NIC name.
+ """
+ cidr = self.settings_obj[network].get('cidr')
+ nic_name = self.settings_obj[network].get('bridged_interface')
+
+ if cidr:
+ cidr = ipaddress.ip_network(self.settings_obj[network]['cidr'])
+ self.settings_obj[network]['cidr'] = cidr
+ logging.info("{}_cidr: {}".format(network, cidr))
+ return 0
+ elif nic_name:
+ # If cidr is not specified, we need to know if we should find
+ # IPv6 or IPv4 address on the interface
+ if self.settings_obj[network].get('ipv6') == True:
+ address_family = 6
+ else:
+ address_family = 4
+ nic_interface = ip_utils.get_interface(nic_name, address_family)
+ if nic_interface:
+ self.settings_obj[network]['bridged_interface'] = nic_interface
+ logging.info("{}_bridged_interface: {}".
+ format(network, nic_interface))
+ return 0
+ else:
+ raise NetworkSettingsException("Auto detection failed for {}: "
+ "Unable to find valid ip for "
+ "interface {}"
+ .format(network, nic_name))
+
+ else:
+ raise NetworkSettingsException("Auto detection failed for {}: "
+ "either bridge_interface or cidr "
+ "must be specified"
+ .format(network))
+
+ def _config_ip_range(self, network, setting, start_offset=None,
+ end_offset=None, count=None):
+ """
+ Configures IP range for a given setting.
+
+ If the setting is already specified, no change will be made.
+
+ The spec for start_offset, end_offset and count are identical to
+ ip_utils.get_ip_range.
+ """
+ ip_range = self.settings_obj[network].get(setting)
+ interface = self.settings_obj[network].get('bridged_interface')
+
+ if not ip_range:
+ cidr = self.settings_obj[network].get('cidr')
+ ip_range = ip_utils.get_ip_range(start_offset=start_offset,
+ end_offset=end_offset,
+ count=count,
+ cidr=cidr,
+ interface=interface)
+ self.settings_obj[network][setting] = ip_range
+
+ logging.info("{}_{}: {}".format(network, setting, ip_range))
+
+ def _config_ip(self, network, setting, offset):
+ """
+ Configures IP for a given setting.
+
+ If the setting is already specified, no change will be made.
+
+ The spec for offset is identical to ip_utils.get_ip
+ """
+ ip = self.settings_obj[network].get(setting)
+ interface = self.settings_obj[network].get('bridged_interface')
+
+ if not ip:
+ cidr = self.settings_obj[network].get('cidr')
+ ip = ip_utils.get_ip(offset, cidr, interface)
+ self.settings_obj[network][setting] = ip
+
+ logging.info("{}_{}: {}".format(network, setting, ip))
+
+ def _config_optional_settings(self, network):
+ """
+ Configures optional settings:
+ - admin_network:
+ - provisioner_ip
+ - dhcp_range
+ - introspection_range
+ - public_network:
+ - provisioner_ip
+ - floating_ip
+ - gateway
+ """
+ if network == ADMIN_NETWORK:
+ self._config_ip(network, 'provisioner_ip', 1)
+ self._config_ip_range(network=network, setting='dhcp_range',
+ start_offset=2, count=9)
+ self._config_ip_range(network=network,
+ setting='introspection_range',
+ start_offset=11, count=9)
+ elif network == PUBLIC_NETWORK:
+ self._config_ip(network, 'provisioner_ip', 1)
+ self._config_ip_range(network=network,
+ setting='floating_ip',
+ end_offset=2, count=20)
+ self._config_gateway(network)
+
+ def _config_gateway(self, network):
+ """
+ Configures gateway setting for a given network.
+
+ If cidr is specified, we always use the first address in the address
+ space for gateway. Otherwise, we detect the system gateway.
+ """
+ gateway = self.settings_obj[network].get('gateway')
+ interface = self.settings_obj[network].get('bridged_interface')
+
+ if not gateway:
+ cidr = self.settings_obj[network].get('cidr')
+ if cidr:
+ gateway = ip_utils.get_ip(1, cidr)
+ else:
+ gateway = ip_utils.find_gateway(interface)
+
+ if gateway:
+ self.settings_obj[network]['gateway'] = gateway
+ else:
+ raise NetworkSettingsException("Failed to set gateway")
+
+ logging.info("{}_gateway: {}".format(network, gateway))
+
+
+ def dump_bash(self, path=None):
+ """
+ Prints settings for bash consumption.
+
+ If optional path is provided, bash string will be written to the file
+ instead of stdout.
+ """
+ bash_str = ''
+ for network in self.enabled_network_list:
+ for key, value in self.settings_obj[network].items():
+ bash_str += "{}_{}={}\n".format(network, key, value)
+ bash_str += "enabled_network_list='{}'\n" \
+ .format(' '.join(self.enabled_network_list))
+ if path:
+ with open(path, 'w') as file:
+ file.write(bash_str)
+ else:
+ print(bash_str)
+
+
+class NetworkSettingsException(Exception):
+ def __init__(self, value):
+ self.value = value
+
+ def __str__(self):
+ return self.value