e2be32767c6dc50d3f513c70cdef43111f00e352
2 from charmhelpers
.core
.hookenv
import (
10 from charms
.reactive
import (
19 from charms
import router
25 @hook('config-changed')
26 def validate_config():
29 If the ssh credentials are available, we'll act as a proxy charm.
30 Otherwise, we execute against the unit we're deployed on to.
32 if all(k
in cfg
for k
in ['pass', 'vpe-router', 'user']):
33 routerip
= cfg
['vpe-router']
37 if routerip
and user
and passwd
:
38 # Assumption: this will be a root user
39 out
, err
= router
.ssh(['whoami'], routerip
,
41 if out
.strip() != user
:
42 remove_state('vpe.configured')
43 status_set('blocked', 'vpe is not configured')
44 raise Exception('invalid credentials')
46 # Set the router's hostname
48 if user
== 'root' and 'hostname' in cfg
:
49 hostname
= cfg
['hostname']
50 out
, err
= router
.ssh(['hostname', hostname
],
53 out
, err
= router
.ssh(['sed',
55 '"s/hostname.*$/hostname %s/"'
57 '/usr/admin/global/hostname.sh'
61 set_state('vpe.configured')
62 status_set('active', 'ready!')
64 remove_state('vpe.configured')
65 status_set('blocked', 'vpe is not configured')
66 except subprocess
.CalledProcessError
as e
:
67 remove_state('vpe.configured')
68 status_set('blocked', 'validation failed: %s' % e
)
69 log('Command failed: %s (%s)' %
70 (' '.join(e
.cmd
), str(e
.output
)))
73 except Exception as e
:
75 remove_state('vpe.configured')
76 status_set('blocked', 'validation failed: %s' % e
)
79 @when_not('vpe.configured')
82 'vpe.add-corporation',
83 'vpe.connect-domains',
84 'vpe.delete-domain-connections',
85 'vpe.remove-corporation',
86 'vpe.configure-interface',
90 if helpers
.any_states(*actions
):
91 action_fail('VPE is not configured')
93 status_set('blocked', 'vpe is not configured')
97 # We may want to make this configurable via config setting
98 ospfd
= '/usr/local/bin/ospfd'
101 (stdout
, stderr
) = router
._run
(['touch',
102 '/usr/admin/global/ospfd.conf'])
103 (stdout
, stderr
) = router
._run
([ospfd
, '-d', '-f',
104 '/usr/admin/global/ospfd.conf'])
105 except subprocess
.CalledProcessError
as e
:
106 log('Command failed: %s (%s)' %
107 (' '.join(e
.cmd
), str(e
.output
)))
110 def configure_ospf(domain
, cidr
, area
, subnet_cidr
, subnet_area
, enable
=True):
111 """Configure the OSPF service"""
113 # Check to see if the OSPF daemon is running, and start it if not
115 (stdout
, stderr
) = router
._run
(['pgrep', 'ospfd'])
116 except subprocess
.CalledProcessError
as e
:
117 # If pgrep fails, the process wasn't found.
119 log('Command failed (ospfd not running): %s (%s)' %
120 (' '.join(e
.cmd
), str(e
.output
)))
126 vrfctl
= '/usr/local/bin/vrfctl'
127 vtysh
= '/usr/local/bin/vtysh'
129 (stdout
, stderr
) = router
._run
([vrfctl
, 'list'])
132 for line
in stdout
.split('\n'):
134 domain_id
= int(line
[3:5])
139 '"configure terminal"',
141 '"router ospf %d vr %d"' % (domain_id
, domain_id
),
143 '"%s network %s area %s"' % (upordown
, cidr
, area
),
145 '"%s network %s area %s"' % (upordown
,
151 log("Invalid domain id")
152 except subprocess
.CalledProcessError
as e
:
153 action_fail('Command failed: %s (%s)' %
154 (' '.join(e
.cmd
), str(e
.output
)))
156 remove_state('vpe.configure-interface')
157 status_set('active', 'ready!')
160 @when('vpe.configured')
161 @when('vpe.configure-interface')
162 def configure_interface():
164 Configure an ethernet interface
166 iface_name
= action_get('iface-name')
167 cidr
= action_get('cidr')
172 # Add may fail, but change seems to add or update
173 router
.ip('address', 'change', cidr
, 'dev', iface_name
)
174 except subprocess
.CalledProcessError
as e
:
175 action_fail('Command failed: %s (%s)' %
176 (' '.join(e
.cmd
), str(e
.output
)))
179 remove_state('vpe.configure-interface')
180 status_set('active', 'ready!')
183 router
.ip('link', 'set', 'dev', iface_name
, 'up')
184 except subprocess
.CalledProcessError
as e
:
185 action_fail('Command failed: %s (%s)' %
186 (' '.join(e
.cmd
), str(e
.output
)))
188 remove_state('vpe.configure-interface')
189 status_set('active', 'ready!')
192 @when('vpe.configured')
193 @when('vpe.add-corporation')
194 def add_corporation():
196 Create and Activate the network corporation
198 domain_name
= action_get('domain-name')
199 iface_name
= action_get('iface-name')
200 # HACK: python's list, used deeper, throws an exception on ints in a tuple
201 vlan_id
= str(action_get('vlan-id'))
202 cidr
= action_get('cidr')
203 area
= action_get('area')
204 subnet_cidr
= action_get('subnet-cidr')
205 subnet_area
= action_get('subnet-area')
207 iface_vlanid
= '%s.%s' % (iface_name
, vlan_id
)
209 status_set('maintenance', 'adding corporation {}'.format(domain_name
))
212 Attempt to run all commands to add the network corporation. If any step
213 fails, abort and call `delete_corporation()` to undo.
217 $ ip link add link eth3 name eth3.103 type vlan id 103
231 $ ip netns add domain
238 $ ip link set dev eth3.103 netns corpB
250 router
._run
(['ifconfig', iface_name
, 'up'])
253 $ ip netns exec corpB ip link set dev eth3.103 up
266 $ ip netns exec corpB ip address add 10.0.1.1/24 dev eth3.103
268 mask
= cidr
.split("/")[1]
269 ip
= '%s/%s' % (area
, mask
)
280 configure_ospf(domain_name
, cidr
, area
, subnet_cidr
, subnet_area
, True)
282 except subprocess
.CalledProcessError
as e
:
284 action_fail('Command failed: %s (%s)' %
285 (' '.join(e
.cmd
), str(e
.output
)))
287 remove_state('vpe.add-corporation')
288 status_set('active', 'ready!')
291 @when('vpe.configured')
292 @when('vpe.delete-corporation')
293 def delete_corporation():
295 domain_name
= action_get('domain-name')
296 cidr
= action_get('cidr')
297 area
= action_get('area')
298 subnet_cidr
= action_get('subnet-cidr')
299 subnet_area
= action_get('subnet-area')
301 status_set('maintenance', 'deleting corporation {}'.format(domain_name
))
305 Remove all tunnels defined for this domain
307 $ ip netns exec domain_name ip tun show
309 | grep -v "remote any"
330 # `p` should be a tuple of (stdout, stderr)
331 tunnels
= p
[0].split('\n')
333 for tunnel
in tunnels
:
336 $ ip netns exec domain_name ip link set $tunnel_name down
348 except subprocess
.CalledProcessError
as e
:
349 log('Command failed: %s (%s)' %
350 (' '.join(e
.cmd
), str(e
.output
)))
355 $ ip netns exec domain_name ip tunnel del $tunnel_name
366 except subprocess
.CalledProcessError
as e
:
367 log('Command failed: %s (%s)' %
368 (' '.join(e
.cmd
), str(e
.output
)))
372 Remove all interfaces associated to the domain
374 $ ip netns exec domain_name ifconfig | grep mtu | cut -d":" -f1
387 ifaces
= p
[0].split('\n')
392 $ ip netns exec domain_name ip link set $iface down
404 except subprocess
.CalledProcessError
as e
:
405 log('Command failed: %s (%s)' %
406 (' '.join(e
.cmd
), str(e
.output
)))
412 router
._run
(['ifconfig', iface
, 'down'])
413 except subprocess
.CalledProcessError
as e
:
414 log('Command failed: %s (%s)' %
415 (' '.join(e
.cmd
), str(e
.output
)))
420 $ ip link del dev $iface
428 except subprocess
.CalledProcessError
as e
:
429 log('Command failed: %s (%s)' %
430 (' '.join(e
.cmd
), str(e
.output
)))
437 $ ip netns del domain_name
444 except subprocess
.CalledProcessError
as e
:
445 log('Command failed: %s (%s)' % (' '.join(e
.cmd
), str(e
.output
)))
449 configure_ospf(domain_name
,
455 except subprocess
.CalledProcessError
as e
:
456 action_fail('Command failed: %s (%s)' %
457 (' '.join(e
.cmd
), str(e
.output
)))
461 log('delete-corporation failed.')
465 remove_state('vpe.delete-corporation')
466 status_set('active', 'ready!')
469 @when('vpe.configured')
470 @when('vpe.connect-domains')
471 def connect_domains():
481 'internal-remote-ip',
487 config
[p
] = action_get(p
)
489 status_set('maintenance', 'connecting domains')
493 $ ip tunnel add tunnel_name mode gre local local_ip remote remote_ip
494 dev iface_name key tunnel_key csum
499 config
['tunnel-name'],
501 config
['tunnel-type'],
507 config
['iface-name'],
509 config
['tunnel-key'],
513 except subprocess
.CalledProcessError
as e
:
514 log('Command failed (retrying with ip tunnel change): %s (%s)' %
515 (' '.join(e
.cmd
), str(e
.output
)))
518 If the tunnel already exists (like gre0) and can't be deleted,
519 modify it instead of trying to add it.
524 config
['tunnel-name'],
526 config
['tunnel-type'],
532 config
['iface-name'],
534 config
['tunnel-key'],
537 except subprocess
.CalledProcessError
as e
:
538 delete_domain_connection()
539 action_fail('Command failed: %s (%s)' %
540 (' '.join(e
.cmd
), str(e
.output
)))
542 remove_state('vpe.connect-domains')
543 status_set('active', 'ready!')
547 $ ip link set dev tunnel_name netns domain_name
553 config
['tunnel-name'],
555 config
['domain-name']
559 $ ip netns exec domain_name ip link set dev tunnel_name up
564 config
['domain-name'],
569 config
['tunnel-name'],
574 $ ip netns exec domain_name ip address add internal_local_ip peer
575 internal_remote_ip dev tunnel_name
580 config
['domain-name'],
584 config
['internal-local-ip'],
586 config
['internal-remote-ip'],
588 config
['tunnel-name']
590 except subprocess
.CalledProcessError
as e
:
591 delete_domain_connection()
592 action_fail('Command failed: %s (%s)' %
593 (' '.join(e
.cmd
), str(e
.output
)))
595 remove_state('vpe.connect-domains')
596 status_set('active', 'ready!')
599 @when('vpe.configured')
600 @when('vpe.delete-domain-connection')
601 def delete_domain_connection():
602 ''' Remove the tunnel to another router where the domain is present '''
603 domain
= action_get('domain-name')
604 tunnel_name
= action_get('tunnel-name')
606 status_set('maintenance', 'deleting domain connection: {}'.format(domain
))
612 $ ip netns exec domain_name ip link set tunnel_name down
622 except subprocess
.CalledProcessError
as e
:
623 action_fail('Command failed: %s (%s)' %
624 (' '.join(e
.cmd
), str(e
.output
)))
628 $ ip netns exec domain_name ip tunnel del tunnel_name
637 except subprocess
.CalledProcessError
as e
:
638 action_fail('Command failed: %s (%s)' %
639 (' '.join(e
.cmd
), str(e
.output
)))
643 remove_state('vpe.delete-domain-connection')
644 status_set('active', 'ready!')