Adds allow_service parameter to bigip_selfip (#2808)

This parameter can be used to open up access to (among other things)
the mgmt address of a BIG-IP. It is necessary for configuring bigips
in an HA configuration.
reviewable/pr18780/r1
Tim Rupp 8 years ago committed by René Moser
parent 638e25bead
commit ef6eb80c12

@ -28,6 +28,12 @@ options:
description: description:
- The IP addresses for the new self IP. This value is ignored upon update - The IP addresses for the new self IP. This value is ignored upon update
as addresses themselves cannot be changed after they are created. as addresses themselves cannot be changed after they are created.
allow_service:
description:
- Configure port lockdown for the Self IP. By default, the Self IP has a
"default deny" policy. This can be changed to allow TCP and UDP ports
as well as specific protocols. This list should contain C(protocol):C(port)
values.
name: name:
description: description:
- The self IP to create. - The self IP to create.
@ -90,9 +96,76 @@ EXAMPLES = '''
user: "admin" user: "admin"
validate_certs: "no" validate_certs: "no"
delegate_to: localhost delegate_to: localhost
- name: Allow management web UI to be accessed on this Self IP
bigip_selfip:
name: "self1"
password: "secret"
server: "lb.mydomain.com"
state: "absent"
user: "admin"
validate_certs: "no"
allow_service:
- "tcp:443"
delegate_to: localhost
- name: Allow HTTPS and SSH access to this Self IP
bigip_selfip:
name: "self1"
password: "secret"
server: "lb.mydomain.com"
state: "absent"
user: "admin"
validate_certs: "no"
allow_service:
- "tcp:443"
- "tpc:22"
delegate_to: localhost
- name: Allow all services access to this Self IP
bigip_selfip:
name: "self1"
password: "secret"
server: "lb.mydomain.com"
state: "absent"
user: "admin"
validate_certs: "no"
allow_service:
- all
delegate_to: localhost
- name: Allow only GRE and IGMP protocols access to this Self IP
bigip_selfip:
name: "self1"
password: "secret"
server: "lb.mydomain.com"
state: "absent"
user: "admin"
validate_certs: "no"
allow_service:
- gre:0
- igmp:0
delegate_to: localhost
- name: Allow all TCP, but no other protocols access to this Self IP
bigip_selfip:
name: "self1"
password: "secret"
server: "lb.mydomain.com"
state: "absent"
user: "admin"
validate_certs: "no"
allow_service:
- tcp:0
delegate_to: localhost
''' '''
RETURN = ''' RETURN = '''
allow_service:
description: Services that allowed via this Self IP
returned: changed
type: list
sample: ['igmp:0','tcp:22','udp:53']
address: address:
description: The address for the Self IP description: The address for the Self IP
returned: created returned: created
@ -144,6 +217,8 @@ except ImportError:
FLOAT = ['enabled', 'disabled'] FLOAT = ['enabled', 'disabled']
DEFAULT_TG = 'traffic-group-local-only' DEFAULT_TG = 'traffic-group-local-only'
ALLOWED_PROTOCOLS = ['eigrp', 'egp', 'gre', 'icmp', 'igmp', 'igp', 'ipip',
'l2tp', 'ospf', 'pim', 'tcp', 'udp']
class BigIpSelfIp(object): class BigIpSelfIp(object):
@ -188,6 +263,9 @@ class BigIpSelfIp(object):
Therefore, this method will transform the data from the BIG-IP into a Therefore, this method will transform the data from the BIG-IP into a
format that is more easily consumable by the rest of the class and the format that is more easily consumable by the rest of the class and the
parameters that are supported by the module. parameters that are supported by the module.
:return: List of values currently stored in BIG-IP, formatted for use
in this class.
""" """
p = dict() p = dict()
name = self.params['name'] name = self.params['name']
@ -207,16 +285,97 @@ class BigIpSelfIp(object):
p['traffic_group'] = str(r.trafficGroup) p['traffic_group'] = str(r.trafficGroup)
if hasattr(r, 'vlan'): if hasattr(r, 'vlan'):
p['vlan'] = str(r.vlan) p['vlan'] = str(r.vlan)
if hasattr(r, 'allowService'):
if r.allowService == 'all':
p['allow_service'] = set(['all'])
else:
p['allow_service'] = set([str(x) for x in r.allowService])
else:
p['allow_service'] = set(['none'])
p['name'] = name p['name'] = name
return p return p
def verify_services(self):
"""Verifies that a supplied service string has correct format
The string format for port lockdown is PROTOCOL:PORT. This method
will verify that the provided input matches the allowed protocols
and the port ranges before submitting to BIG-IP.
The only allowed exceptions to this rule are the following values
* all
* default
* none
These are special cases that are handled differently in the API.
"all" is set as a string, "default" is set as a one item list, and
"none" removes the key entirely from the REST API.
:raises F5ModuleError:
"""
result = []
for svc in self.params['allow_service']:
if svc in ['all', 'none', 'default']:
result = [svc]
break
tmp = svc.split(':')
if tmp[0] not in ALLOWED_PROTOCOLS:
raise F5ModuleError(
"The provided protocol '%s' is invalid" % (tmp[0])
)
try:
port = int(tmp[1])
except Exception:
raise F5ModuleError(
"The provided port '%s' is not a number" % (tmp[1])
)
if port < 0 or port > 65535:
raise F5ModuleError(
"The provided port '%s' must be between 0 and 65535"
% (port)
)
else:
result.append(svc)
return set(result)
def fmt_services(self, services):
"""Returns services formatted for consumption by f5-sdk update
The BIG-IP endpoint for services takes different values depending on
what you want the "allowed services" to be. It can be any of the
following
- a list containing "protocol:port" values
- the string "all"
- a null value, or None
This is a convenience function to massage the values the user has
supplied so that they are formatted in such a way that BIG-IP will
accept them and apply the specified policy.
:param services: The services to format. This is always a Python set
:return:
"""
result = list(services)
if result[0] == 'all':
return 'all'
elif result[0] == 'none':
return None
else:
return list(services)
def update(self): def update(self):
changed = False changed = False
svcs = []
params = dict() params = dict()
current = self.read() current = self.read()
check_mode = self.params['check_mode'] check_mode = self.params['check_mode']
address = self.params['address'] address = self.params['address']
allow_service = self.params['allow_service']
name = self.params['name'] name = self.params['name']
netmask = self.params['netmask'] netmask = self.params['netmask']
partition = self.params['partition'] partition = self.params['partition']
@ -278,6 +437,14 @@ class BigIpSelfIp(object):
'The specified VLAN was not found' 'The specified VLAN was not found'
) )
if allow_service is not None:
svcs = self.verify_services()
if 'allow_service' in current:
if svcs != current['allow_service']:
params['allowService'] = self.fmt_services(svcs)
else:
params['allowService'] = self.fmt_services(svcs)
if params: if params:
changed = True changed = True
params['name'] = name params['name'] = name
@ -285,6 +452,8 @@ class BigIpSelfIp(object):
if check_mode: if check_mode:
return changed return changed
self.cparams = camel_dict_to_snake_dict(params) self.cparams = camel_dict_to_snake_dict(params)
if svcs:
self.cparams['allow_service'] = list(svcs)
else: else:
return changed return changed
@ -298,6 +467,25 @@ class BigIpSelfIp(object):
return True return True
def get_vlans(self): def get_vlans(self):
"""Returns formatted list of VLANs
The VLAN values stored in BIG-IP are done so using their fully
qualified name which includes the partition. Therefore, "correct"
values according to BIG-IP look like this
/Common/vlan1
This is in contrast to the formats that most users think of VLANs
as being stored as
vlan1
To provide for the consistent user experience while not turfing
BIG-IP, we need to massage the values that are provided by the
user so that they include the partition.
:return: List of vlans formatted with preceeding partition
"""
partition = self.params['partition'] partition = self.params['partition']
vlans = self.api.tm.net.vlans.get_collection() vlans = self.api.tm.net.vlans.get_collection()
return [str("/" + partition + "/" + x.name) for x in vlans] return [str("/" + partition + "/" + x.name) for x in vlans]
@ -305,8 +493,10 @@ class BigIpSelfIp(object):
def create(self): def create(self):
params = dict() params = dict()
svcs = []
check_mode = self.params['check_mode'] check_mode = self.params['check_mode']
address = self.params['address'] address = self.params['address']
allow_service = self.params['allow_service']
name = self.params['name'] name = self.params['name']
netmask = self.params['netmask'] netmask = self.params['netmask']
partition = self.params['partition'] partition = self.params['partition']
@ -353,10 +543,17 @@ class BigIpSelfIp(object):
'The specified VLAN was not found' 'The specified VLAN was not found'
) )
if allow_service is not None:
svcs = self.verify_services()
params['allowService'] = self.fmt_services(svcs)
params['name'] = name params['name'] = name
params['partition'] = partition params['partition'] = partition
self.cparams = camel_dict_to_snake_dict(params) self.cparams = camel_dict_to_snake_dict(params)
if svcs:
self.cparams['allow_service'] = list(svcs)
if check_mode: if check_mode:
return True return True
@ -416,6 +613,7 @@ def main():
meta_args = dict( meta_args = dict(
address=dict(required=False, default=None), address=dict(required=False, default=None),
allow_service=dict(type='list', default=None),
name=dict(required=True), name=dict(required=True),
netmask=dict(required=False, default=None), netmask=dict(required=False, default=None),
traffic_group=dict(required=False, default=None), traffic_group=dict(required=False, default=None),

Loading…
Cancel
Save