Skip to content

Commit 647e8f7

Browse files
author
Gustavo Serra Scalet
committed
Network test: add ip forward and ForwardingRule test
1 parent 5b8920f commit 647e8f7

File tree

4 files changed

+205
-16
lines changed

4 files changed

+205
-16
lines changed

daisy_workflows/image_test/linux_common/utils.py

Lines changed: 101 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,7 @@ def __init__(self, compute, instance, ssh_user='tester'):
208208
user: string, the user to create ssh keys and perform ssh tests.
209209
"""
210210
self.zone = self.FetchMetadataDefault('zone')
211+
self.region = self.zone[:-2] # clears the "-[a-z]$" of the zone
211212
self.project = self.FetchMetadataDefault('project')
212213
self.compute = compute
213214
self.instance = instance
@@ -404,6 +405,9 @@ def AttachDisk(self, instance, disk_name):
404405
Args:
405406
instance: string, the name of the instance to attach disk.
406407
disk_name: string, the name of the disks to be attached.
408+
409+
Returns:
410+
response: dict, the request's response.
407411
"""
408412
body = {'source': 'projects/%s/zones/%s/disks/%s' % (
409413
self.project, self.zone, disk_name)}
@@ -431,6 +435,9 @@ def DetachDisk(self, instance, device_name):
431435
Args:
432436
instance: string, the name of the instance to detach disk.
433437
device_name: string, the device name of the disk to be detached.
438+
439+
Returns:
440+
response: dictionary, the request's response.
434441
"""
435442
request = self.compute.instances().detachDisk(
436443
project=self.project, zone=self.zone, instance=instance,
@@ -444,14 +451,105 @@ def Wait(self, response):
444451
Args:
445452
response: dict, a request's response
446453
"""
447-
operation = response[u'name']
454+
def _OperationGetter(response):
455+
operation = response[u'name']
456+
if response.get(u'zone'):
457+
return self.compute.zoneOperations().get(
458+
project=self.project, zone=self.zone, operation=operation)
459+
elif response.get(u'region'):
460+
return self.compute.regionOperations().get(
461+
project=self.project, region=self.region, operation=operation)
462+
else:
463+
return self.compute.globalOperations().get(
464+
project=self.project, operation=operation)
465+
448466
while True:
449-
result = self.compute.zoneOperations().get(
450-
project=self.project, zone=self.zone, operation=operation).execute()
467+
result = _OperationGetter(response).execute()
451468

452469
if result['status'] == 'DONE':
453470
if 'error' in result:
454471
raise Exception(result['error'])
455472
return result
456473

457474
time.sleep(1)
475+
476+
def AddTargetInstance(self, name, instance):
477+
"""Creates a target instance to be used by a forwarding rule
478+
479+
Args:
480+
name: string, the name of the target instance.
481+
instance: string, the instance name to be mapped by the target instnace.
482+
483+
Returns:
484+
response: dictionary, the request's response.
485+
"""
486+
body = {
487+
"name": name,
488+
"instance": 'projects/%s/zones/%s/instances/%s' % (
489+
self.project, self.zone, instance)
490+
}
491+
492+
return self.compute.targetInstances().insert(
493+
project=self.project, zone=self.zone, body=body).execute()
494+
495+
def DeleteTargetInstance(self, name):
496+
"""Deletes a target instance
497+
498+
Args:
499+
name: string, the name of the target instance.
500+
501+
Returns:
502+
response: dictionary, the request's response.
503+
"""
504+
return self.compute.targetInstances().delete(
505+
project=self.project, zone=self.zone, targetInstance=name).execute()
506+
507+
def AddForwardingRule(self, name, target, ports="80", protocol="TCP"):
508+
"""Creates a forwarding rule
509+
510+
Args:
511+
name: string, the name of this forwarding rule.
512+
target: string, the target instance name to be used.
513+
ports: list of int or string, the ports used.
514+
protocol: string, the ip protocol (e.g: TCP, UDP)
515+
516+
Returns:
517+
response: dictionary, the request's response.
518+
"""
519+
body = {
520+
"name": name,
521+
"portRange": ",".join(ports) if type(ports) is list else ports,
522+
"target": 'projects/%s/zones/%s/targetInstances/%s' % (
523+
self.project, self.zone, target),
524+
"LoadBalancingScheme": "INTERNAL"
525+
}
526+
527+
return self.compute.forwardingRules().insert(
528+
project=self.project, region=self.region, body=body).execute()
529+
530+
def GetForwardingRuleIP(self, name):
531+
"""Retrieves a forwarding rule ip
532+
533+
Args:
534+
name: string, the name of the forwarding rule.
535+
536+
Returns:
537+
response: string, the forwarding rule ip.
538+
"""
539+
request = self.compute.forwardingRules().get(
540+
project=self.project, region=self.region, forwardingRule=name)
541+
response = request.execute()
542+
return response[u"IPAddress"]
543+
544+
def DeleteForwardingRule(self, name):
545+
"""Deletes a forwarding rule
546+
547+
Args:
548+
name: string, the name of the forwarding rule.
549+
550+
Returns:
551+
response: dictionary, the request's response.
552+
"""
553+
request = self.compute.forwardingRules().delete(
554+
project=self.project, region=self.region, forwardingRule=name)
555+
return request.execute()

daisy_workflows/image_test/network/network-testee.sh

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,16 @@
1313
# See the License for the specific language governing permissions and
1414
# limitations under the License.
1515

16+
# Verify DNS connections if there is a INSTANCE variable defined
17+
18+
# Verify VM to VM DNS connection
19+
[ -n "$INSTANCE" ] && getent hosts $INSTANCE
20+
1621
# Verify VM to external DNS connection
17-
getent hosts www.google.com
22+
[ -n "$INSTANCE" ] && getent hosts www.google.com
1823

19-
# Signalize wait-for-testee-instance that instance is ready
20-
[ $? -ne 0 ] && echo "DNS_Failed" || echo "BOOTED"
24+
# Signalize wait-for-instance that instance is ready or error occurred
25+
[ $? -ne 0 ] && [ -n "$INSTANCE" ] && echo "DNS_Failed" > /dev/console || echo "BOOTED" > /dev/console
2126

2227
# Serve a file server that prints the hostname when requesting "/hostname"
2328
mkdir /server

daisy_workflows/image_test/network/network-tester.py

Lines changed: 48 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,10 @@
1616
import urllib2
1717

1818

19-
import utils
2019
import genips
21-
22-
utils.AptGetInstall(['python-pip'])
23-
utils.Execute(['pip', 'install', '--upgrade', 'google-api-python-client'])
20+
from google import auth
21+
from googleapiclient import discovery
22+
import utils
2423

2524

2625
def GetHostname(addr, timeout=10):
@@ -51,15 +50,59 @@ def TestIPAlias(testee, ip_alias, ip_mask):
5150
pass
5251

5352

53+
class RequestError(Exception):
54+
def __init__(self, name, instance, ip_range):
55+
self.name = name
56+
self.instance = instance
57+
self.ip_range = ip_range
58+
59+
def __str__(self):
60+
return "%s didn't redirect ip %s to instance %s" % (
61+
self.name, self.ip_range, self.instance)
62+
63+
64+
def TestForwardingRule(MD, instance):
65+
target_name = instance + "-target"
66+
MD.Wait(MD.AddTargetInstance(target_name, instance))
67+
try:
68+
rule_name = instance + "-rule"
69+
MD.Wait(MD.AddForwardingRule(rule_name, target_name))
70+
try:
71+
ip = MD.GetForwardingRuleIP(rule_name)
72+
if GetHostname(instance) != GetHostname(ip):
73+
raise RequestError(rule_name, instance, ip)
74+
finally:
75+
MD.Wait(MD.DeleteForwardingRule(rule_name))
76+
finally:
77+
MD.Wait(MD.DeleteTargetInstance(target_name))
78+
79+
5480
def main():
5581
MM = utils.MetadataManager
56-
# Verify VM to VM DNS connection
5782
testee = MM.FetchMetadataDefault('testee')
5883

5984
# Verify IP aliasing is working
6085
TestIPAlias(testee, MM.FetchMetadataDefault('alias_ip'),
6186
MM.FetchMetadataDefault('alias_ip_mask'))
6287

88+
# Ensure routes are added when enabling IP Forwarding
89+
credentials, _ = auth.default()
90+
compute = utils.GetCompute(discovery, credentials)
91+
92+
# (Negative test)
93+
# The testee instance has IP Forward disabled, hence it should not accept the
94+
# traffic from a different IP even if the router is sending traffic to it.
95+
# This was a test to guarantee that behavior.
96+
try:
97+
TestForwardingRule(MM(compute, testee), testee)
98+
raise Exception("Forwarding rule shouldn't work for non-ipforward machine")
99+
except (RequestError, urllib2.URLError):
100+
pass
101+
102+
# (Positive test)
103+
testee_ipforward = MM.FetchMetadataDefault('testee_ipforward')
104+
TestForwardingRule(MM(compute, testee_ipforward), testee_ipforward)
105+
63106

64107
if __name__ == '__main__':
65108
utils.RunTest(main)

daisy_workflows/image_test/network/network.wf.json

Lines changed: 48 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,14 @@
2121
}
2222
]
2323
},
24+
"create-testee-ipforward-disk": {
25+
"CreateDisks": [
26+
{
27+
"Name": "disk-testee-ipforward-img",
28+
"SourceImage": "${source_image}"
29+
}
30+
]
31+
},
2432
"create-tester-disk": {
2533
"CreateDisks": [
2634
{
@@ -44,7 +52,21 @@
4452
"AccessConfigs": [{"Type": "ONE_TO_ONE_NAT"}],
4553
"AliasIpRanges": [{"ipCidrRange": "${alias_ip}/${alias_ip_mask}"}]
4654
}
47-
]
55+
],
56+
"canIpForward": false
57+
}
58+
]
59+
},
60+
"create-testee-ipforward-instance": {
61+
"CreateInstances": [
62+
{
63+
"Name": "inst-network-testee-ipforward",
64+
"RealName": "inst-network-testee-ipforward-${DATETIME}-${ID}",
65+
"Disks": [{"Source": "disk-testee-ipforward-img"}],
66+
"Metadata": {
67+
"startup-script": "INSTANCE=inst-network-testee-${DATETIME}-${ID}; ${SOURCE:testee.sh}"
68+
},
69+
"canIpForward": true
4870
}
4971
]
5072
},
@@ -60,16 +82,35 @@
6082
"debian_install_google_api_python_client": "yes",
6183
"testee": "inst-network-testee-${DATETIME}-${ID}",
6284
"alias_ip": "${alias_ip}",
63-
"alias_ip_mask": "${alias_ip_mask}"
64-
}
85+
"alias_ip_mask": "${alias_ip_mask}",
86+
"testee_ipforward": "inst-network-testee-ipforward-${DATETIME}-${ID}",
87+
"zone": "${ZONE}",
88+
"project": "${PROJECT}"
89+
},
90+
"Scopes": [
91+
"https://www.googleapis.com/auth/devstorage.read_only",
92+
"https://www.googleapis.com/auth/compute"
93+
]
6594
}
6695
]
6796
},
6897
"wait-for-testee-instance": {
6998
"Timeout": "5m",
7099
"WaitForInstancesSignal": [
71-
{
100+
{
72101
"Name": "inst-network-testee",
102+
"SerialOutput": {
103+
"Port": 1,
104+
"SuccessMatch": "BOOTED"
105+
}
106+
}
107+
]
108+
},
109+
"wait-for-testee-ipforward-instance": {
110+
"Timeout": "5m",
111+
"WaitForInstancesSignal": [
112+
{
113+
"Name": "inst-network-testee-ipforward",
73114
"SerialOutput": {
74115
"Port": 1,
75116
"SuccessMatch": "BOOTED",
@@ -95,7 +136,9 @@
95136
"Dependencies": {
96137
"create-testee-instance": ["create-testee-disk"],
97138
"wait-for-testee-instance": ["create-testee-instance"],
98-
"create-tester-instance": ["wait-for-testee-instance", "create-tester-disk"],
139+
"create-testee-ipforward-instance": ["create-testee-ipforward-disk", "wait-for-testee-instance"],
140+
"wait-for-testee-ipforward-instance": ["create-testee-ipforward-instance"],
141+
"create-tester-instance": ["wait-for-testee-ipforward-instance", "create-tester-disk"],
99142
"wait-for-testee-check": ["create-tester-instance"]
100143
}
101144
}

0 commit comments

Comments
 (0)