Make functional test importable and stop depending on DIB code

With integration tests on the radar we no longer need to do
integration checking between ramdisk code and inspector here.

Also copy integration test script from functest to devstack.
Once we update the gate to use the new script, the whole functest
directory should be deleted.

Change-Id: Ic29e954fac1c535cb4f83d46ccea9ebfb80f840c
Closes-Bug: #1468748
This commit is contained in:
Dmitry Tantsur 2015-06-29 13:49:05 +02:00 committed by Dmitry Tantsur
parent 9654a066de
commit def1f3700c
16 changed files with 143 additions and 321 deletions

89
devstack/exercise.sh Executable file
View File

@ -0,0 +1,89 @@
#!/bin/bash
set -eux
INTROSPECTION_SLEEP=${INTROSPECTION_SLEEP:-30}
EXPECTED_CPU_ARCH=${EXPECTED_CPU_ARCH:-x86_64}
EXPECTED_CPUS=${EXPECTED_CPUS:-1}
EXPECTED_MIN_LOCAL_GB=${EXPECTED_MIN_LOCAL_GB:-1}
EXPECTED_MIN_MEMORY_MB=${EXPECTED_MIN_MEMORY_MB:-512}
ironic_url=$(keystone endpoint-get --service baremetal | tail -n +4 | head -n -1 | tr '|' ' ' | awk '{ print $2; }')
if [ -z "$ironic_url" ]; then
echo "Cannot find Ironic URL"
exit 1
fi
nodes=$(ironic node-list | tail -n +4 | head -n -1 | tr '|' ' ' | awk '{ print $1; }')
if [ -z "$nodes" ]; then
echo "No nodes found in Ironic"
exit 1
fi
for uuid in $nodes; do
for p in cpus cpu_arch memory_mb local_gb; do
ironic node-update $uuid remove properties/$p > /dev/null || true
done
done
for uuid in $nodes; do
# TODO(dtantsur): use Ironic API instead
openstack baremetal introspection start $uuid
done
current_nodes=$nodes
temp_nodes=
while true; do
sleep $INTROSPECTION_SLEEP
for uuid in $current_nodes; do
finished=$(openstack baremetal introspection status $uuid -f value -c finished)
if [ "$finished" = "True" ]; then
error=$(openstack baremetal introspection status $uuid -f value -c error)
if [ "$error" != "None" ]; then
echo "Introspection for $uuid failed: $error"
exit 1
fi
else
temp_nodes="$temp_nodes $uuid"
fi
done
if [ "$temp_nodes" = "" ]; then
echo "Introspection done"
break
else
current_nodes=$temp_nodes
temp_nodes=
fi
done
# NOTE(dtantsur): it's hard to get JSON field from Ironic client output, using
# HTTP API and JQ instead.
token=$(keystone token-get | grep ' id ' | tr '|' ' ' | awk '{ print $2; }')
function curl_ir {
curl -H "X-Auth-Token: $token" -X $1 "$ironic_url/$2"
}
for uuid in $nodes; do
node_json=$(curl_ir GET v1/nodes/$uuid)
properties=$(echo $node_json | jq '.properties')
echo Properties for $uuid: $properties
if [ "$(echo $properties | jq -r '.cpu_arch')" != "$EXPECTED_CPU_ARCH" ]; then
echo "Expected $EXPECTED_CPU_ARCH"
exit 1
fi
if [ "$(echo $properties | jq -r '.cpus')" != "$EXPECTED_CPUS" ]; then
echo "Expected $EXPECTED_CPUS"
exit 1
fi
if [ "$(echo $properties | jq -r '.local_gb')" -lt "$EXPECTED_MIN_LOCAL_GB" ]; then
echo "Expected at least $EXPECTED_MIN_LOCAL_GB"
exit 1
fi
if [ "$(echo $properties | jq -r '.memory_mb')" -lt "$EXPECTED_MIN_MEMORY_MB" ]; then
echo "Expected at least $EXPECTED_MIN_MEMORY_MB"
exit 1
fi
done
echo "Validation passed"

View File

@ -1,108 +0,0 @@
processor : 0
vendor_id : GenuineIntel
cpu family : 6
model : 58
model name : Intel(R) Core(TM) i7-3520M CPU @ 2.90GHz
stepping : 9
microcode : 0x1b
cpu MHz : 2180.550
cache size : 4096 KB
physical id : 0
siblings : 4
core id : 0
cpu cores : 2
apicid : 0
initial apicid : 0
fpu : yes
fpu_exception : yes
cpuid level : 13
wp : yes
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc aperfmperf eagerfpu pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm ida arat epb pln pts dtherm tpr_shadow vnmi flexpriority ept vpid fsgsbase smep erms xsaveopt
bugs :
bogomips : 5786.46
clflush size : 64
cache_alignment : 64
address sizes : 36 bits physical, 48 bits virtual
power management:
processor : 1
vendor_id : GenuineIntel
cpu family : 6
model : 58
model name : Intel(R) Core(TM) i7-3520M CPU @ 2.90GHz
stepping : 9
microcode : 0x1b
cpu MHz : 2409.718
cache size : 4096 KB
physical id : 0
siblings : 4
core id : 0
cpu cores : 2
apicid : 1
initial apicid : 1
fpu : yes
fpu_exception : yes
cpuid level : 13
wp : yes
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc aperfmperf eagerfpu pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm ida arat epb pln pts dtherm tpr_shadow vnmi flexpriority ept vpid fsgsbase smep erms xsaveopt
bugs :
bogomips : 5786.46
clflush size : 64
cache_alignment : 64
address sizes : 36 bits physical, 48 bits virtual
power management:
processor : 2
vendor_id : GenuineIntel
cpu family : 6
model : 58
model name : Intel(R) Core(TM) i7-3520M CPU @ 2.90GHz
stepping : 9
microcode : 0x1b
cpu MHz : 2140.562
cache size : 4096 KB
physical id : 0
siblings : 4
core id : 1
cpu cores : 2
apicid : 2
initial apicid : 2
fpu : yes
fpu_exception : yes
cpuid level : 13
wp : yes
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc aperfmperf eagerfpu pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm ida arat epb pln pts dtherm tpr_shadow vnmi flexpriority ept vpid fsgsbase smep erms xsaveopt
bugs :
bogomips : 5786.46
clflush size : 64
cache_alignment : 64
address sizes : 36 bits physical, 48 bits virtual
power management:
processor : 3
vendor_id : GenuineIntel
cpu family : 6
model : 58
model name : Intel(R) Core(TM) i7-3520M CPU @ 2.90GHz
stepping : 9
microcode : 0x1b
cpu MHz : 2350.699
cache size : 4096 KB
physical id : 0
siblings : 4
core id : 1
cpu cores : 2
apicid : 3
initial apicid : 3
fpu : yes
fpu_exception : yes
cpuid level : 13
wp : yes
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl xtopology nonstop_tsc aperfmperf eagerfpu pni pclmulqdq dtes64 monitor ds_cpl vmx smx est tm2 ssse3 cx16 xtpr pdcm pcid sse4_1 sse4_2 x2apic popcnt tsc_deadline_timer aes xsave avx f16c rdrand lahf_lm ida arat epb pln pts dtherm tpr_shadow vnmi flexpriority ept vpid fsgsbase smep erms xsaveopt
bugs :
bogomips : 5786.46
clflush size : 64
cache_alignment : 64
address sizes : 36 bits physical, 48 bits virtual
power management:

View File

@ -1,50 +0,0 @@
echo "# dmidecode 2.12
SMBIOS 2.7 present.
Handle 0x0007, DMI type 16, 23 bytes
Physical Memory Array
Location: System Board Or Motherboard
Use: System Memory
Error Correction Type: None
Maximum Capacity: 16 GB
Error Information Handle: Not Provided
Number Of Devices: 2
Handle 0x0008, DMI type 17, 34 bytes
Memory Device
Array Handle: 0x0007
Error Information Handle: Not Provided
Total Width: 64 bits
Data Width: 64 bits
Size: 8192 MB
Form Factor: SODIMM
Set: None
Locator: ChannelA-DIMM0
Bank Locator: BANK 0
Type: DDR3
Type Detail: Synchronous
Speed: 1600 MHz
Manufacturer: Samsung
Asset Tag: None
Rank: Unknown
Configured Clock Speed: 1600 MHz
Handle 0x0009, DMI type 17, 34 bytes
Memory Device
Array Handle: 0x0007
Error Information Handle: Not Provided
Total Width: 64 bits
Data Width: 64 bits
Size: 4096 MB
Form Factor: SODIMM
Set: None
Locator: ChannelB-DIMM0
Bank Locator: BANK 2
Type: DDR3
Type Detail: Synchronous
Speed: 1600 MHz
Manufacturer: Hynix/Hyundai
Asset Tag: None
Rank: Unknown
Configured Clock Speed: 1600 MHz
"

10
functest/env/fdisk vendored
View File

@ -1,10 +0,0 @@
echo "Disk /dev/sda: 465.8 GiB, 500107862016 bytes, 976773168 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 4096 bytes
I/O size (minimum/optimal): 4096 bytes / 4096 bytes
Disklabel type: dos
Disk identifier: 0x000b9da0
Device Boot Start End Blocks Id System
/dev/sda1 * 2048 1026047 512000 83 Linux
/dev/sda2 1026048 976773119 487873536 83 Linux"

View File

@ -1,9 +0,0 @@
if [[ "$1" = "discoverd_callback_url" ]];
then
echo http://127.0.0.1:5050/v1/continue
elif [[ "$1" = "BOOTIF" ]];
then
echo 01-11-22-33-44-55-66
else
echo
fi

21
functest/env/ip vendored
View File

@ -1,21 +0,0 @@
if [ "x$3" == "xem1" ];
then
echo "1: em1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
link/ether 11:22:33:44:55:66 brd ff:ff:ff:ff:ff:ff
inet 192.168.0.15/24 brd 192.168.0.255 scope global dynamic wlp3s0
valid_lft 78195sec preferred_lft 78195sec"
fi
if [ "x$3" == "xem2" ];
then
echo "2: em2: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc pfifo_fast state DOWN group default qlen 1000
link/ether 66:55:44:33:22:11 brd ff:ff:ff:ff:ff:ff"
fi
if [ "x$3" == "xem3" ];
then
echo "3: em3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
link/ether 66:55:44:33:22:11 brd ff:ff:ff:ff:ff:ff
inet 192.168.0.16/24 brd 192.168.0.255 scope global dynamic wlp3s0
valid_lft 78195sec preferred_lft 78195sec"
fi

25
functest/env/ipmitool vendored
View File

@ -1,25 +0,0 @@
if [[ "$1" == "lan" ]]; then
echo "Set in Progress : Set Complete
Auth Type Support : NONE MD2 MD5 PASSWORD
Auth Type Enable : Callback :
: User : MD2 MD5 PASSWORD
: Operator : MD2 MD5 PASSWORD
: Admin : MD2 MD5 PASSWORD
: OEM :
IP Address Source : Static Address
IP Address : 1.2.3.4
Subnet Mask : 255.255.254.0
MAC Address : 11:22:11:22:11:22
SNMP Community String : public
IP Header : TTL=0x40 Flags=0x40 Precedence=0x00 TOS=0x10
BMC ARP Control : ARP Responses Enabled, Gratuitous ARP Disabled
Gratituous ARP Intrvl : 2.0 seconds
Default Gateway IP : 1.2.3.1
Default Gateway MAC : 00:00:00:00:00:00
Backup Gateway IP : 0.0.0.0
Backup Gateway MAC : 00:00:00:00:00:00
TFTP Server IP : 0.0.0.0
NTP Server IP : 1.1.0.0"
else
echo $@ >> ipmi_calls.txt
fi

24
functest/env/lscpu vendored
View File

@ -1,24 +0,0 @@
echo "Architecture: x86_64
CPU op-mode(s): 32-bit, 64-bit
Byte Order: Little Endian
CPU(s): 4
On-line CPU(s) list: 0-3
Thread(s) per core: 2
Core(s) per socket: 2
Socket(s): 1
NUMA node(s): 1
Vendor ID: GenuineIntel
CPU family: 6
Model: 58
Model name: Intel(R) Core(TM) i7-3520M CPU @ 2.90GHz
Stepping: 9
CPU MHz: 1486.250
CPU max MHz: 3600.0000
CPU min MHz: 1200.0000
BogoMIPS: 5786.46
Virtualization: VT-x
L1d cache: 32K
L1i cache: 32K
L2 cache: 256K
L3 cache: 4096K
NUMA node0 CPU(s): 0-3"

View File

@ -1 +0,0 @@
exit 1

View File

@ -1 +0,0 @@
true

1
functest/env/sleep vendored
View File

@ -1 +0,0 @@
true

View File

@ -1,2 +0,0 @@
echo "TROUBLESHOOTING!"
exit 1

View File

@ -181,7 +181,7 @@ def create_ssl_context():
return context
def main(args=sys.argv[1:]): # pragma: no cover
def main(args=sys.argv[1:], in_functional_test=False): # pragma: no cover
CONF(args, project='ironic-inspector')
debug = CONF.debug
@ -193,7 +193,7 @@ def main(args=sys.argv[1:]): # pragma: no cover
logging.getLogger('ironicclient.common.http').setLevel(
logging.INFO if debug else logging.ERROR)
app_kwargs = {'debug': debug,
app_kwargs = {'debug': debug and not in_functional_test,
'host': CONF.listen_address,
'port': CONF.listen_port}

View File

@ -11,16 +11,12 @@
# See the License for the specific language governing permissions and
# limitations under the License.
from __future__ import print_function
import eventlet
eventlet.monkey_patch()
import json
import os
import re
import shutil
import stat
import subprocess
import sys
import tempfile
import unittest
@ -46,86 +42,63 @@ manage_firewall = False
enable_setting_ipmi_credentials = True
[DEFAULT]
database = %(db_file)s
debug = True
"""
ROOT = './functest/env'
RAMDISK = ("https://raw.githubusercontent.com/openstack/diskimage-builder/"
"master/elements/ironic-discoverd-ramdisk/"
"init.d/80-ironic-discoverd-ramdisk")
JQ = "https://stedolan.github.io/jq/download/linux64/jq"
DEFAULT_SLEEP = 2
class Test(base.NodeTest):
def setUp(self):
super(Test, self).setUp()
self.node.properties.clear()
self.cli = utils.get_client()
self.cli.reset_mock()
self.cli.node.get.return_value = self.node
self.cli.node.update.return_value = self.node
self.temp = tempfile.mkdtemp()
self.addCleanup(lambda: shutil.rmtree(self.temp))
self.env = os.path.join(self.temp, 'env')
shutil.copytree(ROOT, self.env)
net_ifaces = os.path.join(self.env, 'net')
os.mkdir(net_ifaces)
for fname in ('lo', 'em1', 'em2', 'em3'):
open(os.path.join(net_ifaces, fname), 'wb').close()
ramdisk_url = os.environ.get('RAMDISK_SOURCE', RAMDISK)
if re.match(r'^https?://', ramdisk_url):
ramdisk = requests.get(ramdisk_url).content
else:
with open(ramdisk_url, 'rb') as f:
ramdisk = f.read()
ramdisk = ramdisk.replace('/proc/cpuinfo', os.path.join(self.env,
'cpuinfo.txt'))
ramdisk = ramdisk.replace('/sys/class/net', net_ifaces)
self.ramdisk_sh = os.path.join(self.env, 'ramdisk')
with open(self.ramdisk_sh, 'wb') as f:
f.write(ramdisk)
# jq is not on gate slaves
jq_path = os.path.join(self.env, 'jq')
with open(jq_path, 'wb') as f:
jq = requests.get(JQ, stream=True).raw
shutil.copyfileobj(jq, f)
os.chmod(jq_path, stat.S_IRWXU)
old_wd = os.getcwd()
os.chdir(self.temp)
self.addCleanup(lambda: os.chdir(old_wd))
# These properties come from fake tools in functest/env
# https://github.com/openstack/ironic-inspector/blob/master/HTTP-API.rst # noqa
self.data = {
'cpus': 4,
'cpu_arch': 'x86_64',
'memory_mb': 12288,
'local_gb': 464,
'interfaces': {
'eth1': {'mac': self.macs[0], 'ip': '1.2.1.2'},
'eth2': {'mac': '12:12:21:12:21:12'},
'eth3': {'mac': self.macs[1], 'ip': '1.2.1.1'},
},
'boot_interface': '01-' + self.macs[0].replace(':', '-'),
'ipmi_address': self.bmc_address,
}
self.patch = [
{'op': 'add', 'path': '/properties/cpus', 'value': '4'},
{'path': '/properties/cpu_arch', 'value': 'x86_64', 'op': 'add'},
{'op': 'add', 'path': '/properties/memory_mb', 'value': '12288'},
{'path': '/properties/local_gb', 'value': '464', 'op': 'add'}
]
self.node.power_state = 'power off'
def call_ramdisk(self):
env = os.environ.copy()
env['PATH'] = self.env + ':' + env.get('PATH', '')
subprocess.check_call(['/bin/bash', '-eux', self.ramdisk_sh], env=env)
res = requests.post('http://127.0.0.1:5050/v1/continue',
data=json.dumps(self.data))
res.raise_for_status()
return res
def test_bmc(self):
client.introspect(self.uuid, auth_token='token')
eventlet.greenthread.sleep(1)
eventlet.greenthread.sleep(DEFAULT_SLEEP)
self.cli.node.set_power_state.assert_called_once_with(self.uuid,
'reboot')
status = client.get_status(self.uuid, auth_token='token')
self.assertEqual({'finished': False, 'error': None}, status)
self.call_ramdisk()
eventlet.greenthread.sleep(1)
res = self.call_ramdisk()
self.assertEqual({'uuid': self.uuid}, res.json())
eventlet.greenthread.sleep(DEFAULT_SLEEP)
self.cli.node.update.assert_any_call(self.uuid, self.patch)
self.cli.port.create.assert_called_once_with(
@ -144,14 +117,18 @@ class Test(base.NodeTest):
self.node.maintenance = True
client.introspect(self.uuid, auth_token='token',
new_ipmi_username='admin', new_ipmi_password='pwd')
eventlet.greenthread.sleep(1)
eventlet.greenthread.sleep(DEFAULT_SLEEP)
self.assertFalse(self.cli.node.set_power_state.called)
status = client.get_status(self.uuid, auth_token='token')
self.assertEqual({'finished': False, 'error': None}, status)
self.call_ramdisk()
eventlet.greenthread.sleep(1)
res = self.call_ramdisk()
res_json = res.json()
self.assertEqual('admin', res_json['ipmi_username'])
self.assertEqual('pwd', res_json['ipmi_password'])
self.assertTrue(res_json['ipmi_setup_credentials'])
eventlet.greenthread.sleep(DEFAULT_SLEEP)
self.cli.node.update.assert_any_call(self.uuid, self.patch)
self.cli.node.update.assert_any_call(self.uuid, patch_credentials)
@ -161,11 +138,6 @@ class Test(base.NodeTest):
status = client.get_status(self.uuid, auth_token='token')
self.assertEqual({'finished': True, 'error': None}, status)
with open(os.path.join(self.temp, 'ipmi_calls.txt'), 'rb') as f:
lines = f.readlines()
self.assertIn('user set name 2 admin\n', lines)
self.assertIn('user set password 2 pwd\n', lines)
@mock.patch.object(utils, 'check_auth')
@mock.patch.object(utils, 'get_client')
@ -178,14 +150,28 @@ def run(client_mock, keystone_mock):
fp.write(CONF % {'db_file': db_file})
eventlet.greenthread.spawn_n(main.main,
args=['--config-file', conf_file])
args=['--config-file', conf_file],
in_functional_test=True)
eventlet.greenthread.sleep(1)
# Wait for service to start up to 30 seconds
for i in range(10):
try:
requests.get('http://127.0.0.1:5050/v1')
except requests.ConnectionError:
if i == 9:
raise
print('Service did not start yet')
eventlet.greenthread.sleep(3)
else:
break
suite = unittest.TestLoader().loadTestsFromTestCase(Test)
res = unittest.TextTestRunner().run(suite)
sys.exit(0 if res.wasSuccessful() else 1)
# Make sure all processes finished executing
eventlet.greenthread.sleep(1)
return 0 if res.wasSuccessful() else 1
finally:
shutil.rmtree(d)
if __name__ == '__main__':
run()
sys.exit(run())

View File

@ -5,3 +5,4 @@ coverage>=3.6
doc8 # Apache-2.0
hacking<0.11,>=0.10.0
mock>=1.0
python-ironic-inspector-client>=1.0.1

View File

@ -39,10 +39,8 @@ deps =
-r{toxinidir}/requirements.txt
-r{toxinidir}/test-requirements.txt
-r{toxinidir}/plugin-requirements.txt
# TODO(dtantsur): move to test-reqs once it's in global-requirements
python-ironic-inspector-client
commands =
python functest/run.py
python -m ironic_inspector.test.functional
[testenv:genconfig]
commands =