55presence of default rules for egress traffic is checked.
66"""
77
8- import openstack
9- import os
108import argparse
9+ import os
10+
11+ import openstack
1112from openstack .exceptions import ResourceNotFound
1213
1314SG_NAME = "default-test-sg"
1415DESCRIPTION = "default-test-sg"
1516
1617
17- def connect (cloud_name : str ) -> openstack .connection .Connection :
18- """Create a connection to an OpenStack cloud
19-
20- :param string cloud_name:
21- The name of the configuration to load from clouds.yaml.
22-
23- :returns: openstack.connnection.Connection
24- """
25- return openstack .connect (
26- cloud = cloud_name ,
27- )
28-
29-
3018def count_ingress_egress (rules , short = False ):
3119 """
3220 counts all verall ingress rules and egress rules, depending on the requested testing mode
@@ -39,107 +27,60 @@ def count_ingress_egress(rules, short=False):
3927 """
4028 ingress_rules = 0
4129 egress_rules = 0
42- if not short :
43- egress_ipv4_default_sg = 0
44- egress_ipv4_custom_sg = 0
45- egress_ipv6_default_sg = 0
46- egress_ipv6_custom_sg = 0
47- else :
48- egress_ipv4 = 0
49- egress_ipv6 = 0
30+ egress_vars = {'IPv4' : {}, 'IPv6' : {}}
31+ for key , value in egress_vars .items ():
32+ value ['default' ] = 0
33+ if not short :
34+ value ['custom' ] = 0
5035 if not rules :
5136 print ("No default security group rules defined." )
52- else :
53- for rule in rules :
54- direction = rule ["direction " ]
55- ethertype = rule [ "ethertype" ]
37+ for rule in rules :
38+ direction = rule [ "direction" ]
39+ ethertype = rule ["ethertype " ]
40+ if direction == "ingress" :
5641 if not short :
57- r_custom_sg = rule ["used_in_non_default_sg" ]
58- r_default_sg = rule ["used_in_default_sg" ]
59- if direction == "ingress" :
60- ingress_rules += 1
61- if not short :
62- # we allow ingress from the same security group
63- # but only for the default security group
64- r_group_id = rule .remote_group_id
65- if r_group_id == "PARENT" and not r_custom_sg :
66- ingress_rules -= 1
67- elif direction == "egress" and ethertype == "IPv4" :
68- egress_rules += 1
69- if not short :
70- if rule .remote_ip_prefix :
71- # this rule does not allow traffic to all external ips
72- continue
73- if r_custom_sg :
74- egress_ipv4_custom_sg += 1
75- if r_default_sg :
76- egress_ipv4_default_sg += 1
77- else :
78- egress_ipv4 += 1
79- elif direction == "egress" and ethertype == "IPv6" :
80- egress_rules += 1
81- if not short :
82- if rule .remote_ip_prefix :
83- # this rule does not allow traffic to all external ips
84- continue
85- if r_custom_sg :
86- egress_ipv6_custom_sg += 1
87- if r_default_sg :
88- egress_ipv6_default_sg += 1
89- else :
90- egress_ipv6 += 1
91- if not egress_rules > 0 :
92- raise ValueError (
93- f"Expected to have more than { egress_rules } egress rules present."
94- )
95- if not short :
96- var_list = [
97- egress_ipv4_default_sg ,
98- egress_ipv4_custom_sg ,
99- egress_ipv6_default_sg ,
100- egress_ipv6_custom_sg ,
101- ]
102- else :
103- var_list = [
104- egress_ipv4 ,
105- egress_ipv6 ,
106- ]
42+ # we allow ingress from the same security group
43+ # but only for the default security group
44+ r_group_id = rule .remote_group_id
45+ if r_group_id == "PARENT" and not rule ["used_in_non_default_sg" ]:
46+ continue
47+ ingress_rules += 1
48+ elif direction == "egress" and ethertype in egress_vars :
49+ egress_rules += 1
50+ if short :
51+ egress_vars [ethertype ]['default' ] += 1
52+ continue
53+ if rule .remote_ip_prefix :
54+ # this rule does not allow traffic to all external ips
55+ continue
56+ # note: these two are not mutually exclusive
57+ if rule ["used_in_default_sg" ]:
58+ egress_vars [ethertype ]['default' ] += 1
59+ if rule ["used_in_non_default_sg" ]:
60+ egress_vars [ethertype ]['custom' ] += 1
10761 # test whether there are no unallowed ingress rules
108- if not ingress_rules == 0 :
62+ if ingress_rules :
10963 raise ValueError (
11064 f"Expected no default ingress rules for security groups, "
11165 f"But there are { ingress_rules } ingress rules. "
112- f"There should be only none."
11366 )
11467 # test whether all expected egress rules are present
115- if not all (var > 0 for var in var_list ):
68+ missing = [(key , key2 ) for key , val in egress_vars .items () for key2 , val2 in val .items () if not val2 ]
69+ if missing :
11670 raise ValueError (
117- "Not all expected egress rules are present. "
11871 "Expected rules for egress for IPv4 and IPv6 "
119- "both for default and custom security groups."
120- )
121- return ingress_rules , egress_rules
122-
123-
124- def test_rules (cloud_name : str ):
125- try :
126- connection = connect (cloud_name )
127- rules = connection .network .default_security_group_rules ()
128- except Exception as e :
129- print (str (e ))
130- raise Exception (
131- f"Connection to cloud '{ cloud_name } ' was not successful. "
132- f"The default Security Group Rules could not be accessed. "
133- f"Please check your cloud connection and authorization."
72+ "both for default and custom security groups. "
73+ f"Missing rule types: { ', ' .join (str (x ) for x in missing )} "
13474 )
135- if not any (rule for rule in rules ):
136- raise
137- ingress_rules , egress_rules = count_ingress_egress (rules )
138- result_dict = {
75+ return {
13976 "Unallowed Ingress Rules" : ingress_rules ,
14077 "Egress Rules" : egress_rules ,
14178 }
142- return result_dict
79+
80+
81+ def test_rules (connection : openstack .connection .Connection ):
82+ rules = connection .network .default_security_group_rules ()
83+ return count_ingress_egress (rules )
14384
14485
14586def create_security_group (conn , sg_name : str = SG_NAME , description : str = DESCRIPTION ):
@@ -166,30 +107,13 @@ def delete_security_group(conn, sg_id):
166107 print (f"Security group { sg_id } was not deleted successfully" f"Exception: { e } " )
167108
168109
169- def altern_test_rules (cloud_name : str ):
110+ def altern_test_rules (connection : openstack .connection .Connection ):
111+ sg_id = create_security_group (connection )
170112 try :
171- connection = connect (cloud_name )
172- except Exception as e :
173- print (str (e ))
174- raise Exception (
175- f"Connection to cloud '{ cloud_name } ' was not successful. "
176- f"The default Security Group Rules could not be accessed. "
177- f"Please check your cloud connection and authorization."
178- )
179- try :
180- sg_id = create_security_group (connection )
181- rules = connection .network .find_security_group (name_or_id = sg_id )
182- except Exception :
183- print ("Security group was not created successfully." )
184-
185- ingress_rules , egress_rules = count_ingress_egress (rules .security_group_rules , True )
186- delete_security_group (connection , sg_id )
187-
188- result_dict = {
189- "Unallowed Ingress Rules" : ingress_rules ,
190- "Egress Rules" : egress_rules ,
191- }
192- return result_dict
113+ sg = connection .network .find_security_group (name_or_id = sg_id )
114+ return count_ingress_egress (sg .security_group_rules , True )
115+ finally :
116+ delete_security_group (connection , sg_id )
193117
194118
195119def main ():
@@ -209,25 +133,26 @@ def main():
209133 openstack .enable_logging (debug = args .debug )
210134
211135 # parse cloud name for lookup in clouds.yaml
212- cloud = os .environ .get ("OS_CLOUD" , None )
213- if args .os_cloud :
214- cloud = args .os_cloud
136+ cloud = args .os_cloud or os .environ .get ("OS_CLOUD" , None )
215137 if not cloud :
216138 raise ValueError (
217139 "You need to have the OS_CLOUD environment variable set to your cloud "
218140 "name or pass it via --os-cloud"
219141 )
220- try :
221- print (test_rules (cloud ))
222- except ResourceNotFound as e :
223- print (
224- "Ressource could not be found. Openstack components might not be up to date. "
225- "Continuing with still supported test. "
226- f"Error: { e } "
227- )
228- print (altern_test_rules (cloud ))
229- except Exception as e :
230- print (f"Error occured: { e } " )
142+
143+ with openstack .connect (cloud ) as conn :
144+ try :
145+ print (test_rules (conn ))
146+ except ResourceNotFound as e :
147+ print (
148+ "Resource could not be found. OpenStack components might not be up to date. "
149+ "Falling back to old-style test method. "
150+ f"Error: { e } "
151+ )
152+ print (altern_test_rules (conn ))
153+ except Exception as e :
154+ print (f"Error occured: { e } " )
155+ raise
231156
232157
233158if __name__ == "__main__" :
0 commit comments