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 raise Exception('invalid credentials')
44 # Set the router's hostname
46 if user
== 'root' and 'hostname' in cfg
:
47 hostname
= cfg
['hostname']
48 out
, err
= router
.ssh(['hostname', hostname
],
51 out
, err
= router
.ssh(['sed',
53 '"s/hostname.*$/hostname %s/"'
55 '/usr/admin/global/hostname.sh'
60 except subprocess
.CalledProcessError
as e
:
61 log('Command failed: %s (%s)' %
62 (' '.join(e
.cmd
), str(e
.output
)))
65 set_state('vpe.configured')
66 status_set('active', 'ready!')
68 except Exception as e
:
70 remove_state('vpe.configured')
71 status_set('blocked', 'validation failed: %s' % e
)
74 @when_not('vpe.configured')
77 'vpe.add-corporation',
78 'vpe.connect-domains',
79 'vpe.delete-domain-connections',
80 'vpe.remove-corporation',
81 'vpe.configure-interface',
85 if helpers
.any_states(*actions
):
86 action_fail('VPE is not configured')
88 status_set('blocked', 'vpe is not configured')
92 # We may want to make this configurable via config setting
93 ospfd
= '/usr/local/bin/ospfd'
96 (stdout
, stderr
) = router
._run
(['touch',
97 '/usr/admin/global/ospfd.conf'])
98 (stdout
, stderr
) = router
._run
([ospfd
, '-d', '-f',
99 '/usr/admin/global/ospfd.conf'])
100 except subprocess
.CalledProcessError
as e
:
101 log('Command failed: %s (%s)' %
102 (' '.join(e
.cmd
), str(e
.output
)))
105 def configure_ospf(domain
, cidr
, area
, subnet_cidr
, subnet_area
, enable
=True):
106 """Configure the OSPF service"""
108 # Check to see if the OSPF daemon is running, and start it if not
110 (stdout
, stderr
) = router
._run
(['pgrep', 'ospfd'])
111 except subprocess
.CalledProcessError
as e
:
112 # If pgrep fails, the process wasn't found.
114 log('Command failed (ospfd not running): %s (%s)' %
115 (' '.join(e
.cmd
), str(e
.output
)))
121 vrfctl
= '/usr/local/bin/vrfctl'
122 vtysh
= '/usr/local/bin/vtysh'
124 (stdout
, stderr
) = router
._run
([vrfctl
, 'list'])
127 for line
in stdout
.split('\n'):
129 domain_id
= int(line
[3:5])
134 '"configure terminal"',
136 '"router ospf %d vr %d"' % (domain_id
, domain_id
),
138 '"%s network %s area %s"' % (upordown
, cidr
, area
),
140 '"%s network %s area %s"' % (upordown
,
146 log("Invalid domain id")
147 except subprocess
.CalledProcessError
as e
:
148 action_fail('Command failed: %s (%s)' %
149 (' '.join(e
.cmd
), str(e
.output
)))
151 remove_state('vpe.configure-interface')
152 status_set('active', 'ready!')
155 @when('vpe.configured')
156 @when('vpe.configure-interface')
157 def configure_interface():
159 Configure an ethernet interface
161 iface_name
= action_get('iface-name')
162 cidr
= action_get('cidr')
167 # Add may fail, but change seems to add or update
168 router
.ip('address', 'change', cidr
, 'dev', iface_name
)
169 except subprocess
.CalledProcessError
as e
:
170 action_fail('Command failed: %s (%s)' %
171 (' '.join(e
.cmd
), str(e
.output
)))
174 remove_state('vpe.configure-interface')
175 status_set('active', 'ready!')
178 router
.ip('link', 'set', 'dev', iface_name
, 'up')
179 except subprocess
.CalledProcessError
as e
:
180 action_fail('Command failed: %s (%s)' %
181 (' '.join(e
.cmd
), str(e
.output
)))
183 remove_state('vpe.configure-interface')
184 status_set('active', 'ready!')
187 @when('vpe.configured')
188 @when('vpe.add-corporation')
189 def add_corporation():
191 Create and Activate the network corporation
193 domain_name
= action_get('domain-name')
194 iface_name
= action_get('iface-name')
195 # HACK: python's list, used deeper, throws an exception on ints in a tuple
196 vlan_id
= str(action_get('vlan-id'))
197 cidr
= action_get('cidr')
198 area
= action_get('area')
199 subnet_cidr
= action_get('subnet-cidr')
200 subnet_area
= action_get('subnet-area')
202 iface_vlanid
= '%s.%s' % (iface_name
, vlan_id
)
204 status_set('maintenance', 'adding corporation {}'.format(domain_name
))
207 Attempt to run all commands to add the network corporation. If any step
208 fails, abort and call `delete_corporation()` to undo.
212 $ ip link add link eth3 name eth3.103 type vlan id 103
226 $ ip netns add domain
233 $ ip link set dev eth3.103 netns corpB
245 router
._run
(['ifconfig', iface_name
, 'up'])
248 $ ip netns exec corpB ip link set dev eth3.103 up
261 $ ip netns exec corpB ip address add 10.0.1.1/24 dev eth3.103
263 mask
= cidr
.split("/")[1]
264 ip
= '%s/%s' % (area
, mask
)
275 configure_ospf(domain_name
, cidr
, area
, subnet_cidr
, subnet_area
, True)
277 except subprocess
.CalledProcessError
as e
:
279 action_fail('Command failed: %s (%s)' %
280 (' '.join(e
.cmd
), str(e
.output
)))
282 remove_state('vpe.add-corporation')
283 status_set('active', 'ready!')
286 @when('vpe.configured')
287 @when('vpe.delete-corporation')
288 def delete_corporation():
290 domain_name
= action_get('domain-name')
291 cidr
= action_get('cidr')
292 area
= action_get('area')
293 subnet_cidr
= action_get('subnet-cidr')
294 subnet_area
= action_get('subnet-area')
296 status_set('maintenance', 'deleting corporation {}'.format(domain_name
))
300 Remove all tunnels defined for this domain
302 $ ip netns exec domain_name ip tun show
304 | grep -v "remote any"
325 # `p` should be a tuple of (stdout, stderr)
326 tunnels
= p
[0].split('\n')
328 for tunnel
in tunnels
:
331 $ ip netns exec domain_name ip link set $tunnel_name down
343 except subprocess
.CalledProcessError
as e
:
344 log('Command failed: %s (%s)' %
345 (' '.join(e
.cmd
), str(e
.output
)))
350 $ ip netns exec domain_name ip tunnel del $tunnel_name
361 except subprocess
.CalledProcessError
as e
:
362 log('Command failed: %s (%s)' %
363 (' '.join(e
.cmd
), str(e
.output
)))
367 Remove all interfaces associated to the domain
369 $ ip netns exec domain_name ifconfig | grep mtu | cut -d":" -f1
382 ifaces
= p
[0].split('\n')
387 $ ip netns exec domain_name ip link set $iface down
399 except subprocess
.CalledProcessError
as e
:
400 log('Command failed: %s (%s)' %
401 (' '.join(e
.cmd
), str(e
.output
)))
407 router
._run
(['ifconfig', iface
, 'down'])
408 except subprocess
.CalledProcessError
as e
:
409 log('Command failed: %s (%s)' %
410 (' '.join(e
.cmd
), str(e
.output
)))
415 $ ip link del dev $iface
423 except subprocess
.CalledProcessError
as e
:
424 log('Command failed: %s (%s)' %
425 (' '.join(e
.cmd
), str(e
.output
)))
432 $ ip netns del domain_name
439 except subprocess
.CalledProcessError
as e
:
440 log('Command failed: %s (%s)' % (' '.join(e
.cmd
), str(e
.output
)))
444 configure_ospf(domain_name
,
450 except subprocess
.CalledProcessError
as e
:
451 action_fail('Command failed: %s (%s)' %
452 (' '.join(e
.cmd
), str(e
.output
)))
456 log('delete-corporation failed.')
460 remove_state('vpe.delete-corporation')
461 status_set('active', 'ready!')
464 @when('vpe.configured')
465 @when('vpe.connect-domains')
466 def connect_domains():
476 'internal-remote-ip',
482 config
[p
] = action_get(p
)
484 status_set('maintenance', 'connecting domains')
488 $ ip tunnel add tunnel_name mode gre local local_ip remote remote_ip
489 dev iface_name key tunnel_key csum
494 config
['tunnel-name'],
496 config
['tunnel-type'],
502 config
['iface-name'],
504 config
['tunnel-key'],
508 except subprocess
.CalledProcessError
as e
:
509 log('Command failed (retrying with ip tunnel change): %s (%s)' %
510 (' '.join(e
.cmd
), str(e
.output
)))
513 If the tunnel already exists (like gre0) and can't be deleted,
514 modify it instead of trying to add it.
519 config
['tunnel-name'],
521 config
['tunnel-type'],
527 config
['iface-name'],
529 config
['tunnel-key'],
532 except subprocess
.CalledProcessError
as e
:
533 delete_domain_connection()
534 action_fail('Command failed: %s (%s)' %
535 (' '.join(e
.cmd
), str(e
.output
)))
537 remove_state('vpe.connect-domains')
538 status_set('active', 'ready!')
542 $ ip link set dev tunnel_name netns domain_name
548 config
['tunnel-name'],
550 config
['domain-name']
554 $ ip netns exec domain_name ip link set dev tunnel_name up
559 config
['domain-name'],
564 config
['tunnel-name'],
569 $ ip netns exec domain_name ip address add internal_local_ip peer
570 internal_remote_ip dev tunnel_name
575 config
['domain-name'],
579 config
['internal-local-ip'],
581 config
['internal-remote-ip'],
583 config
['tunnel-name']
585 except subprocess
.CalledProcessError
as e
:
586 delete_domain_connection()
587 action_fail('Command failed: %s (%s)' %
588 (' '.join(e
.cmd
), str(e
.output
)))
590 remove_state('vpe.connect-domains')
591 status_set('active', 'ready!')
594 @when('vpe.configured')
595 @when('vpe.delete-domain-connection')
596 def delete_domain_connection():
597 ''' Remove the tunnel to another router where the domain is present '''
598 domain
= action_get('domain-name')
599 tunnel_name
= action_get('tunnel-name')
601 status_set('maintenance', 'deleting domain connection: {}'.format(domain
))
607 $ ip netns exec domain_name ip link set tunnel_name down
617 except subprocess
.CalledProcessError
as e
:
618 action_fail('Command failed: %s (%s)' %
619 (' '.join(e
.cmd
), str(e
.output
)))
623 $ ip netns exec domain_name ip tunnel del tunnel_name
632 except subprocess
.CalledProcessError
as e
:
633 action_fail('Command failed: %s (%s)' %
634 (' '.join(e
.cmd
), str(e
.output
)))
638 remove_state('vpe.delete-domain-connection')
639 status_set('active', 'ready!')