0
# Greengrass Discovery
1
2
Greengrass Core discovery functionality for edge computing scenarios including connectivity information retrieval and core device discovery for local communication setup in AWS IoT Greengrass environments.
3
4
## Capabilities
5
6
### Discovery Client
7
8
The DiscoveryClient enables devices to discover nearby Greengrass Core devices and their connectivity information for local communication.
9
10
#### Client Creation
11
12
```python { .api }
13
class DiscoveryClient:
14
"""
15
Client for Greengrass discovery operations.
16
17
Parameters:
18
- bootstrap: Client bootstrap for networking
19
- socket_options: TCP socket options
20
- tls_context: TLS context for secure connection
21
- region (str): AWS region
22
- gg_server_name (str): Greengrass server name (optional)
23
- proxy_options: HTTP proxy options (optional)
24
"""
25
def __init__(self, bootstrap, socket_options, tls_context, region, gg_server_name=None, proxy_options=None): ...
26
```
27
28
#### Discovery Operations
29
30
```python { .api }
31
def discover(self, thing_name):
32
"""
33
Discover Greengrass Core devices for the specified thing.
34
35
Parameters:
36
- thing_name (str): Name of the IoT thing to discover cores for
37
38
Returns:
39
Future[DiscoverResponse]: Future containing discovery response with core information
40
"""
41
```
42
43
### Data Model Classes
44
45
#### Discovery Response
46
47
```python { .api }
48
@dataclass
49
class DiscoverResponse:
50
"""
51
Response from Greengrass discovery operation containing core information.
52
"""
53
gg_groups: Optional[List[GGGroup]] = None
54
```
55
56
#### Greengrass Group
57
58
```python { .api }
59
@dataclass
60
class GGGroup:
61
"""
62
Greengrass group containing core devices and their connectivity information.
63
"""
64
gg_group_id: Optional[str] = None
65
cores: Optional[List[GGCore]] = None
66
certificate_authorities: Optional[List[str]] = None
67
```
68
69
#### Greengrass Core
70
71
```python { .api }
72
@dataclass
73
class GGCore:
74
"""
75
Greengrass Core device with connectivity endpoints.
76
"""
77
thing_arn: Optional[str] = None
78
connectivity: Optional[List[ConnectivityInfo]] = None
79
```
80
81
#### Connectivity Information
82
83
```python { .api }
84
@dataclass
85
class ConnectivityInfo:
86
"""
87
Connectivity information for reaching a Greengrass Core.
88
"""
89
id: Optional[str] = None
90
host_address: Optional[str] = None
91
port: Optional[int] = None
92
metadata: Optional[str] = None
93
```
94
95
#### Exception Classes
96
97
```python { .api }
98
class DiscoveryException(Exception):
99
"""
100
Exception raised during Greengrass discovery operations.
101
102
Attributes:
103
- message (str): Error message
104
- status_code (int): HTTP status code from discovery service
105
"""
106
def __init__(self, message, status_code=None): ...
107
```
108
109
## Usage Examples
110
111
### Basic Discovery
112
113
```python
114
from awsiot import greengrass_discovery
115
from awscrt import io, auth, http, mqtt
116
import json
117
118
# Create event loop group and host resolver
119
event_loop_group = io.EventLoopGroup(1)
120
host_resolver = io.DefaultHostResolver(event_loop_group)
121
122
# Create client bootstrap
123
client_bootstrap = io.ClientBootstrap(event_loop_group, host_resolver)
124
125
# Create socket options
126
socket_options = io.SocketOptions()
127
128
# Create TLS context with device certificate
129
tls_context_options = io.TlsContextOptions.create_client_with_mtls_from_path(
130
cert_filepath="/path/to/device-certificate.pem.crt",
131
pri_key_filepath="/path/to/device-private.pem.key"
132
)
133
tls_context = io.ClientTlsContext(tls_context_options)
134
135
# Create discovery client
136
discovery_client = greengrass_discovery.DiscoveryClient(
137
bootstrap=client_bootstrap,
138
socket_options=socket_options,
139
tls_context=tls_context,
140
region="us-east-1"
141
)
142
143
def discover_cores(thing_name):
144
"""Discover Greengrass cores for a device."""
145
try:
146
print(f"Discovering Greengrass cores for thing: {thing_name}")
147
148
# Perform discovery
149
discover_future = discovery_client.discover(thing_name)
150
discover_response = discover_future.result(timeout=30)
151
152
print(f"Discovery successful! Found {len(discover_response.gg_groups)} groups")
153
154
for group in discover_response.gg_groups:
155
print(f"\nGroup ID: {group.gg_group_id}")
156
print(f"Certificate Authorities: {len(group.certificate_authorities)}")
157
158
for i, ca_pem in enumerate(group.certificate_authorities):
159
# Save CA certificates for later use
160
with open(f"/tmp/gg-ca-{i}.pem", "w") as f:
161
f.write(ca_pem)
162
print(f" Saved CA certificate {i} to /tmp/gg-ca-{i}.pem")
163
164
print(f"Cores: {len(group.cores)}")
165
for core in group.cores:
166
print(f" Core ARN: {core.thing_arn}")
167
print(f" Connectivity options: {len(core.connectivity)}")
168
169
for conn in core.connectivity:
170
print(f" ID: {conn.id}")
171
print(f" Host: {conn.host_address}:{conn.port}")
172
if conn.metadata:
173
print(f" Metadata: {conn.metadata}")
174
175
return discover_response
176
177
except greengrass_discovery.DiscoveryException as e:
178
print(f"Discovery failed: {e.message}")
179
if e.status_code:
180
print(f"Status code: {e.status_code}")
181
return None
182
except Exception as e:
183
print(f"Discovery error: {e}")
184
return None
185
186
# Discover cores
187
discovery_response = discover_cores("MyGreengrassDevice")
188
```
189
190
### Connect to Discovered Core
191
192
```python
193
from awsiot import greengrass_discovery, mqtt_connection_builder
194
from awscrt import io, mqtt
195
import json
196
197
def connect_to_greengrass_core(thing_name):
198
"""Discover and connect to a Greengrass core."""
199
200
# Perform discovery
201
event_loop_group = io.EventLoopGroup(1)
202
host_resolver = io.DefaultHostResolver(event_loop_group)
203
client_bootstrap = io.ClientBootstrap(event_loop_group, host_resolver)
204
socket_options = io.SocketOptions()
205
206
tls_context_options = io.TlsContextOptions.create_client_with_mtls_from_path(
207
cert_filepath="/path/to/device-certificate.pem.crt",
208
pri_key_filepath="/path/to/device-private.pem.key"
209
)
210
tls_context = io.ClientTlsContext(tls_context_options)
211
212
discovery_client = greengrass_discovery.DiscoveryClient(
213
bootstrap=client_bootstrap,
214
socket_options=socket_options,
215
tls_context=tls_context,
216
region="us-east-1"
217
)
218
219
try:
220
# Discover cores
221
discover_future = discovery_client.discover(thing_name)
222
discover_response = discover_future.result(timeout=30)
223
224
if not discover_response.gg_groups:
225
print("No Greengrass groups found")
226
return None
227
228
# Try to connect to the first available core
229
for group in discover_response.gg_groups:
230
if not group.cores:
231
continue
232
233
# Save CA certificates
234
ca_filepath = "/tmp/gg-ca.pem"
235
with open(ca_filepath, "w") as f:
236
for ca_pem in group.certificate_authorities:
237
f.write(ca_pem)
238
239
for core in group.cores:
240
if not core.connectivity:
241
continue
242
243
# Try each connectivity option
244
for conn_info in core.connectivity:
245
try:
246
print(f"Attempting connection to {conn_info.host_address}:{conn_info.port}")
247
248
# Create MQTT connection to Greengrass core
249
connection = mqtt_connection_builder.mtls_from_path(
250
endpoint=conn_info.host_address,
251
port=conn_info.port,
252
cert_filepath="/path/to/device-certificate.pem.crt",
253
pri_key_filepath="/path/to/device-private.pem.key",
254
ca_filepath=ca_filepath,
255
client_id=thing_name,
256
clean_session=False,
257
keep_alive_secs=30
258
)
259
260
# Connect
261
connect_future = connection.connect()
262
connect_future.result(timeout=10)
263
264
print(f"Successfully connected to Greengrass core at {conn_info.host_address}:{conn_info.port}")
265
266
# Test communication
267
test_communication(connection, thing_name)
268
269
# Disconnect
270
disconnect_future = connection.disconnect()
271
disconnect_future.result()
272
273
return connection
274
275
except Exception as e:
276
print(f"Failed to connect to {conn_info.host_address}:{conn_info.port}: {e}")
277
continue
278
279
print("Failed to connect to any discovered Greengrass cores")
280
return None
281
282
except Exception as e:
283
print(f"Discovery and connection failed: {e}")
284
return None
285
286
def test_communication(connection, thing_name):
287
"""Test communication with Greengrass core."""
288
289
def on_message_received(topic, payload, dup, qos, retain, **kwargs):
290
print(f"Received message on topic {topic}: {payload.decode()}")
291
292
# Subscribe to a local topic
293
local_topic = f"greengrass/device/{thing_name}/data"
294
subscribe_future, _ = connection.subscribe(
295
topic=local_topic,
296
qos=mqtt.QoS.AT_LEAST_ONCE,
297
callback=on_message_received
298
)
299
subscribe_future.result()
300
301
# Publish a test message
302
test_message = {
303
"device_id": thing_name,
304
"message": "Hello from device!",
305
"timestamp": "2023-12-07T10:30:00Z"
306
}
307
308
publish_future, _ = connection.publish(
309
topic=local_topic,
310
payload=json.dumps(test_message),
311
qos=mqtt.QoS.AT_LEAST_ONCE
312
)
313
publish_future.result()
314
315
print("Test message published to Greengrass core")
316
317
# Connect to Greengrass core
318
connection = connect_to_greengrass_core("MyGreengrassDevice")
319
```
320
321
### Discovery with Connection Fallback
322
323
```python
324
from awsiot import greengrass_discovery, mqtt_connection_builder
325
import time
326
327
class GreengrassConnector:
328
def __init__(self, thing_name, cert_path, key_path, region):
329
self.thing_name = thing_name
330
self.cert_path = cert_path
331
self.key_path = key_path
332
self.region = region
333
self.current_connection = None
334
self.discovered_cores = []
335
336
def discover_and_connect(self):
337
"""Discover cores and establish connection with fallback."""
338
339
# Perform discovery
340
discovery_response = self._discover_cores()
341
if not discovery_response:
342
return False
343
344
# Extract all connectivity options
345
self.discovered_cores = []
346
for group in discovery_response.gg_groups:
347
# Save CA certificates
348
ca_filepath = f"/tmp/gg-ca-{group.gg_group_id}.pem"
349
with open(ca_filepath, "w") as f:
350
for ca_pem in group.certificate_authorities:
351
f.write(ca_pem)
352
353
for core in group.cores:
354
for conn_info in core.connectivity:
355
self.discovered_cores.append({
356
'host': conn_info.host_address,
357
'port': conn_info.port,
358
'ca_file': ca_filepath,
359
'core_arn': core.thing_arn,
360
'group_id': group.gg_group_id
361
})
362
363
# Sort by preference (you can implement your own logic)
364
self.discovered_cores.sort(key=lambda x: x['port']) # Prefer certain ports
365
366
# Try to connect to cores in order
367
for core_info in self.discovered_cores:
368
if self._try_connect(core_info):
369
return True
370
371
print("Failed to connect to any discovered Greengrass cores")
372
return False
373
374
def _discover_cores(self):
375
"""Perform Greengrass discovery."""
376
try:
377
# Setup discovery client
378
event_loop_group = io.EventLoopGroup(1)
379
host_resolver = io.DefaultHostResolver(event_loop_group)
380
client_bootstrap = io.ClientBootstrap(event_loop_group, host_resolver)
381
socket_options = io.SocketOptions()
382
383
tls_context_options = io.TlsContextOptions.create_client_with_mtls_from_path(
384
cert_filepath=self.cert_path,
385
pri_key_filepath=self.key_path
386
)
387
tls_context = io.ClientTlsContext(tls_context_options)
388
389
discovery_client = greengrass_discovery.DiscoveryClient(
390
bootstrap=client_bootstrap,
391
socket_options=socket_options,
392
tls_context=tls_context,
393
region=self.region
394
)
395
396
# Perform discovery
397
discover_future = discovery_client.discover(self.thing_name)
398
return discover_future.result(timeout=30)
399
400
except Exception as e:
401
print(f"Discovery failed: {e}")
402
return None
403
404
def _try_connect(self, core_info, timeout=10):
405
"""Try to connect to a specific core."""
406
try:
407
print(f"Trying to connect to {core_info['host']}:{core_info['port']}")
408
409
connection = mqtt_connection_builder.mtls_from_path(
410
endpoint=core_info['host'],
411
port=core_info['port'],
412
cert_filepath=self.cert_path,
413
pri_key_filepath=self.key_path,
414
ca_filepath=core_info['ca_file'],
415
client_id=self.thing_name,
416
clean_session=False,
417
keep_alive_secs=30
418
)
419
420
# Try to connect
421
connect_future = connection.connect()
422
connect_future.result(timeout=timeout)
423
424
self.current_connection = connection
425
print(f"Connected to Greengrass core: {core_info['core_arn']}")
426
return True
427
428
except Exception as e:
429
print(f"Connection failed to {core_info['host']}:{core_info['port']}: {e}")
430
return False
431
432
def reconnect(self):
433
"""Reconnect to Greengrass core with failover."""
434
if self.current_connection:
435
try:
436
self.current_connection.disconnect().result()
437
except:
438
pass
439
440
# Try current core first, then others
441
for core_info in self.discovered_cores:
442
if self._try_connect(core_info):
443
return True
444
445
# If all cores fail, try discovery again
446
print("All known cores failed, attempting rediscovery...")
447
return self.discover_and_connect()
448
449
def disconnect(self):
450
"""Disconnect from current core."""
451
if self.current_connection:
452
try:
453
disconnect_future = self.current_connection.disconnect()
454
disconnect_future.result()
455
print("Disconnected from Greengrass core")
456
except Exception as e:
457
print(f"Error during disconnect: {e}")
458
finally:
459
self.current_connection = None
460
461
# Usage
462
connector = GreengrassConnector(
463
thing_name="MyGreengrassDevice",
464
cert_path="/path/to/device-certificate.pem.crt",
465
key_path="/path/to/device-private.pem.key",
466
region="us-east-1"
467
)
468
469
if connector.discover_and_connect():
470
print("Successfully connected to Greengrass!")
471
472
# Your application logic here
473
try:
474
# Simulate work
475
time.sleep(60)
476
except KeyboardInterrupt:
477
pass
478
finally:
479
connector.disconnect()
480
else:
481
print("Failed to connect to Greengrass")
482
```
483
484
### Discovery Configuration
485
486
To use Greengrass discovery, ensure your device certificate has the necessary permissions:
487
488
1. **IoT Policy**: Attach a policy that allows discovery:
489
490
```json
491
{
492
"Version": "2012-10-17",
493
"Statement": [
494
{
495
"Effect": "Allow",
496
"Action": [
497
"greengrass:Discover"
498
],
499
"Resource": "*"
500
}
501
]
502
}
503
```
504
505
2. **Thing Association**: The device thing must be associated with a Greengrass group.
506
507
3. **Core Configuration**: The Greengrass core must be properly configured and running with connectivity information published.