Skip to content

Commit 7836abd

Browse files
author
Gustavo Serra Scalet
committed
Network test: add ip forward and ForwardingRule test
1 parent d89242c commit 7836abd

File tree

4 files changed

+205
-13
lines changed

4 files changed

+205
-13
lines changed

daisy_workflows/image_test/linux_common/utils.py

Lines changed: 101 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,7 @@ def __init__(self, compute, instance, ssh_user='tester'):
209209
user: string, the user to create ssh keys and perform ssh tests.
210210
"""
211211
self.zone = self.FetchMetadataDefault('zone')
212+
self.region = self.zone[:-2] # clears the "-[a-z]$" of the zone
212213
self.project = self.FetchMetadataDefault('project')
213214
self.compute = compute
214215
self.instance = instance
@@ -405,6 +406,9 @@ def AttachDisk(self, instance, disk_name):
405406
Args:
406407
instance: string, the name of the instance to attach disk.
407408
disk_name: string, the name of the disks to be attached.
409+
410+
Returns:
411+
response: dict, the request's response.
408412
"""
409413
body = {'source': 'projects/%s/zones/%s/disks/%s' % (
410414
self.project, self.zone, disk_name)}
@@ -432,6 +436,9 @@ def DetachDisk(self, instance, device_name):
432436
Args:
433437
instance: string, the name of the instance to detach disk.
434438
device_name: string, the device name of the disk to be detached.
439+
440+
Returns:
441+
response: dictionary, the request's response.
435442
"""
436443
request = self.compute.instances().detachDisk(
437444
project=self.project, zone=self.zone, instance=instance,
@@ -445,14 +452,105 @@ def Wait(self, response):
445452
Args:
446453
response: dict, a request's response
447454
"""
448-
operation = response[u'name']
455+
def _OperationGetter(response):
456+
operation = response[u'name']
457+
if response.get(u'zone'):
458+
return self.compute.zoneOperations().get(
459+
project=self.project, zone=self.zone, operation=operation)
460+
elif response.get(u'region'):
461+
return self.compute.regionOperations().get(
462+
project=self.project, region=self.region, operation=operation)
463+
else:
464+
return self.compute.globalOperations().get(
465+
project=self.project, operation=operation)
466+
449467
while True:
450-
result = self.compute.zoneOperations().get(
451-
project=self.project, zone=self.zone, operation=operation).execute()
468+
result = _OperationGetter(response).execute()
452469

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

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

25+
from googleapiclient import discovery
26+
import oauth2client.client
27+
2528

2629
def GetHostname(addr, timeout=10):
2730
URL = "http://%s/hostname" % addr
@@ -48,15 +51,58 @@ def TestIPAlias(testee, ip_alias, ip_mask):
4851
pass
4952

5053

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

5685
# Verify IP aliasing is working
5786
TestIPAlias(testee, MM.FetchMetadataDefault('alias_ip'),
5887
MM.FetchMetadataDefault('alias_ip_mask'))
5988

89+
# Ensure routes are added when enabling IP Forwarding
90+
compute = utils.GetCompute(discovery, oauth2client.client.GoogleCredentials)
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+
60106

61107
if __name__ == '__main__':
62108
utils.RunTest(main)

daisy_workflows/image_test/network/network.wf.json

Lines changed: 49 additions & 6 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
},
@@ -54,21 +76,40 @@
5476
"Name": "inst-network-tester",
5577
"Disks": [{"Source": "disk-tester-img"}],
5678
"StartupScript": "startup_master_tester",
57-
"metadata": {
79+
"Metadata": {
5880
"test_files_gcs_dir": "${SOURCESPATH}/test_files",
5981
"test_script": "test.py",
6082
"testee": "inst-network-testee-${DATETIME}-${ID}",
6183
"alias_ip": "${alias_ip}",
62-
"alias_ip_mask": "${alias_ip_mask}"
63-
}
84+
"alias_ip_mask": "${alias_ip_mask}",
85+
"testee_ipforward": "inst-network-testee-ipforward-${DATETIME}-${ID}",
86+
"zone": "${ZONE}",
87+
"project": "${PROJECT}"
88+
},
89+
"Scopes": [
90+
"https://www.googleapis.com/auth/devstorage.read_only",
91+
"https://www.googleapis.com/auth/compute"
92+
]
6493
}
6594
]
6695
},
6796
"wait-for-testee-instance": {
6897
"Timeout": "5m",
6998
"WaitForInstancesSignal": [
70-
{
99+
{
71100
"Name": "inst-network-testee",
101+
"SerialOutput": {
102+
"Port": 1,
103+
"SuccessMatch": "BOOTED"
104+
}
105+
}
106+
]
107+
},
108+
"wait-for-testee-ipforward-instance": {
109+
"Timeout": "5m",
110+
"WaitForInstancesSignal": [
111+
{
112+
"Name": "inst-network-testee-ipforward",
72113
"SerialOutput": {
73114
"Port": 1,
74115
"SuccessMatch": "BOOTED",
@@ -94,7 +135,9 @@
94135
"Dependencies": {
95136
"create-testee-instance": ["create-testee-disk"],
96137
"wait-for-testee-instance": ["create-testee-instance"],
97-
"create-tester-instance": ["wait-for-testee-instance", "create-tester-disk"],
138+
"create-testee-ipforward-instance": ["create-testee-ipforward-disk", "wait-for-testee-instance"],
139+
"wait-for-testee-ipforward-instance": ["create-testee-ipforward-instance"],
140+
"create-tester-instance": ["wait-for-testee-ipforward-instance", "create-tester-disk"],
98141
"wait-for-testee-check": ["create-tester-instance"]
99142
}
100143
}

0 commit comments

Comments
 (0)