-
Notifications
You must be signed in to change notification settings - Fork 0
/
bulkInviteEndpoints.py
179 lines (162 loc) · 5.87 KB
/
bulkInviteEndpoints.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
from os import environ
import sys
import argparse
import re
import jwt
from datetime import datetime
import time
import netfoundry
from netfoundry import network_group
#
# inviteZitiEndpoints.py
# blame(ken)
#
# invite endpoint owners by conventional email address where the endpoint name is like
# {first}_{last}-{meta} and email address is like {first}.{last}+{meta}@netfoundry.io
# skipping those that are already registered (.jwt is null) or expired
#
# Supply email addresses one per line on standard input or use --invitees INVITEES_FILE
# python3 ./bulkInviteEndpoints.py \
# --metadata laptop \
# --network-name ZitiBastions \
# --attributes salesEndpoints \
# --credentials credentials.json <<< "[email protected]"
#
# python3 ./bulkInviteEndpoints.py \
# --metadata mobile \
# --network-name ZitiBastions \
# --attributes salesEndpoints \
# --credentials credentials.json <<EOF
# EOF
now = time.time()
# print to stderr
def eprint(*args, **kwargs):
print(*args, file=sys.stderr, **kwargs)
parser = argparse.ArgumentParser()
parser.add_argument(
"-u", "--network-id",
default=None,
help="the UUID of the NF network"
)
parser.add_argument(
"-n", "--network-name",
default=None,
help="the name of the NF network may contain quoted whitespace"
)
parser.add_argument(
"-m", "--metadata",
default=['laptop'],
nargs="+",
required=False,
help="one or more metadata to be appended endpoint names e.g. '-m laptop mobile'"
)
parser.add_argument(
"-a", "--attributes",
default=[],
nargs="+",
required=False,
help="one or more Endpoint role attributes e.g. -a sandbox staging dummyServices"
)
parser.add_argument(
"-i", "--include",
default=None,
help="invite emails matching regex"
)
parser.add_argument(
"-e", "--exclude",
default=None,
help="ignore emails matching regex"
)
parser.add_argument(
"-f", "--invitees",
default=None,
help="filename with one email address per line like {first}.{last}+{meta}@netfoundry.io where optional {meta} is added to the args of --metadata to compose endpoint names like {first}_{last}-{meta}"
)
parser.add_argument(
"-c", "--credentials",
default=None,
help="API account credentials JSON file"
)
args = parser.parse_args()
organization = netfoundry.Organization(
credentials=args.credentials if args.credentials else None
)
network_group = netfoundry.NetworkGroup(organization)
if args.network_name and args.network_id:
raise Exception("ERROR: need one of network-name or network-id")
elif args.network_name:
network = netfoundry.Network(network_group, network_name=args.network_name)
elif args.network_id:
network = netfoundry.Network(network_group, network_id=args.network_id)
else:
raise Exception("ERROR: need one of network-name or network-id")
endpoints = network.endpoints()
if args.invitees:
invitees = open(args.invitees)
else:
invitees = sys.stdin
attributes = list()
# ensure all attributes begin with a # character
for attr in args.attributes:
if not re.match('^[#]', attr):
attr = '#'+attr
attributes.append(attr)
for invitee in invitees:
invitee_email = invitee.rstrip()
invitee_localpart = invitee_email.split('@')[0]
invitee_domainpart = invitee_email.split('@')[1]
invitee_fullname = invitee_localpart.split('+')[0]
invitee_names = invitee_fullname.split('.')
# skip unless included
if args.include and not re.match(args.include, invitee_email):
print('EXCLUDED: {}'.format(invitee_email))
continue
if args.exclude and re.match(args.exclude, invitee_email):
print('EXCLUDED: {}'.format(invitee_email))
continue
# skip unless @netfoundry.io
if not invitee_domainpart == "netfoundry.io":
print('INVALID: illegal email domain {}'.format(invitee_email))
continue
# if the invitee email contains a metadatum
for meta in args.metadata:
endpoint_name = '_'.join(invitee_names)+'-'+meta
# create endpoint unless exists
found = [e for e in endpoints if e['name'] == endpoint_name]
if len(found) > 1:
eprint("ERROR: there are multiple endpoints named {}".format(endpoint_name))
sys.exit(1)
elif len(found) == 1:
endpoint = found[0]
# skip if already registered
if not endpoint['jwt']:
print('REGISTERED: {} skipped'.format(endpoint_name))
continue
else:
# create the endpoint
endpoint = network.create_endpoint(name=endpoint_name,attributes=attributes)
# decode the JWT
expiry_epoch = jwt.decode(endpoint['jwt'], algorithms=["RS256"], options={"verify_signature": False})['exp']
# parse the expiry time
expiry_timestamp = datetime.fromtimestamp(expiry_epoch)
# recreate if expiring in less than 12 hours
if (now+(60*60*12)) > expiry_epoch:
print('EXPIRY: recreating {}'.format(endpoint_name))
try:
network.delete_resource(type="endpoint",id=endpoint['id'])
# recreate the endpoint
endpoint = network.create_endpoint(name=endpoint_name,attributes=attributes)
except Exception as e:
eprint('ERROR: failed to re-create expired endpoint {}'.format(endpoint_name))
raise(e)
try:
network.share_endpoint(invitee_email,endpoint['id'])
# Display an error if something goes wrong.
except Exception as e:
eprint("ERROR: failed to share {} with {}".format(endpoint_name, invitee_email))
# import pdb;pdb.set_trace()
print(e.response['Error']['Message'])
else:
print("SENT {} to {}".format(endpoint_name, invitee_email))