1#!/usr/bin/python 2# 3# Copyright (c) 2019 Yuwei Zhou, <yuwzho@microsoft.com> 4# 5# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) 6 7 8from __future__ import absolute_import, division, print_function 9__metaclass__ = type 10 11 12ANSIBLE_METADATA = {'metadata_version': '1.1', 13 'status': ['preview'], 14 'supported_by': 'community'} 15 16DOCUMENTATION = ''' 17--- 18module: azure_rm_iothub_info 19 20version_added: "2.9" 21 22short_description: Get IoT Hub facts 23 24description: 25 - Get facts for a specific IoT Hub or all IoT Hubs. 26 27options: 28 name: 29 description: 30 - Limit results to a specific resource group. 31 type: str 32 resource_group: 33 description: 34 - The resource group to search for the desired IoT Hub. 35 type: str 36 tags: 37 description: 38 - Limit results by providing a list of tags. Format tags as 'key' or 'key:value'. 39 type: list 40 show_stats: 41 description: 42 - Show the statistics for IoT Hub. 43 - Note this will have network overhead for each IoT Hub. 44 type: bool 45 show_quota_metrics: 46 description: 47 - Get the quota metrics for an IoT hub. 48 - Note this will have network overhead for each IoT Hub. 49 type: bool 50 show_endpoint_health: 51 description: 52 - Get the health for routing endpoints. 53 - Note this will have network overhead for each IoT Hub. 54 type: bool 55 test_route_message: 56 description: 57 - Test routes message. It will be used to test all routes. 58 type: str 59 list_consumer_groups: 60 description: 61 - List the consumer group of the built-in event hub. 62 type: bool 63 list_keys: 64 description: 65 - List the keys of IoT Hub. 66 - Note this will have network overhead for each IoT Hub. 67 type: bool 68extends_documentation_fragment: 69 - azure 70 71author: 72 - Yuwei Zhou (@yuwzho) 73''' 74 75EXAMPLES = ''' 76 - name: Get facts for one IoT Hub 77 azure_rm_iothub_info: 78 name: Testing 79 resource_group: myResourceGroup 80 81 - name: Get facts for all IoT Hubs 82 azure_rm_iothub_info: 83 84 - name: Get facts for all IoT Hubs in a specific resource group 85 azure_rm_iothub_info: 86 resource_group: myResourceGroup 87 88 - name: Get facts by tags 89 azure_rm_iothub_info: 90 tags: 91 - testing 92''' 93 94RETURN = ''' 95azure_iothubs: 96 description: 97 - List of IoT Hub dicts. 98 returned: always 99 type: complex 100 contains: 101 id: 102 description: 103 - Resource ID of the IoT hub. 104 type: str 105 returned: always 106 sample: "/subscriptions/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/resourceGroups/myResourceGroup/providers/Microsoft.Devices/IotHubs/Testing" 107 name: 108 description: 109 - Name of the IoT hub. 110 type: str 111 returned: always 112 sample: Testing 113 resource_group: 114 description: 115 - Resource group of the IoT hub. 116 type: str 117 returned: always 118 sample: myResourceGroup. 119 location: 120 description: 121 - Location of the IoT hub. 122 type: str 123 returned: always 124 sample: eastus 125 unit: 126 description: 127 - Units in the IoT Hub. 128 type: int 129 returned: always 130 sample: 1 131 sku: 132 description: 133 - Pricing tier for Azure IoT Hub. 134 type: str 135 returned: always 136 sample: f1 137 cloud_to_device: 138 description: 139 - Cloud to device message properties. 140 type: complex 141 returned: always 142 contains: 143 max_delivery_count: 144 description: 145 - The number of times the IoT hub attempts to deliver a message on the feedback queue. 146 - "See U(https://docs.microsoft.com/azure/iot-hub/iot-hub-devguide-messaging#cloud-to-device-messages)." 147 type: int 148 returned: always 149 sample: 10 150 ttl_as_iso8601: 151 description: 152 - The period of time for which a message is available to consume before it is expired by the IoT hub. 153 - "See U(https://docs.microsoft.com/azure/iot-hub/iot-hub-devguide-messaging#cloud-to-device-messages)." 154 type: str 155 returned: always 156 sample: "1:00:00" 157 enable_file_upload_notifications: 158 description: 159 - Whether file upload notifications are enabled. 160 type: str 161 returned: always 162 sample: True 163 event_endpoints: 164 description: 165 - Built-in endpoint where to deliver device message. 166 type: complex 167 returned: always 168 contains: 169 endpoint: 170 description: 171 - The Event Hub-compatible endpoint. 172 type: str 173 returned: always 174 sample: "sb://iothub-ns-testing-1478811-9bbc4a15f0.servicebus.windows.net/" 175 partition_count: 176 description: 177 - The number of partitions for receiving device-to-cloud messages in the Event Hub-compatible endpoint. 178 - "See U(https://docs.microsoft.com/azure/iot-hub/iot-hub-devguide-messaging#device-to-cloud-messages)." 179 type: int 180 returned: always 181 sample: 2 182 retention_time_in_days: 183 description: 184 - The retention time for device-to-cloud messages in days. 185 - "See U(https://docs.microsoft.com/azure/iot-hub/iot-hub-devguide-messaging#device-to-cloud-messages)." 186 type: int 187 returned: always 188 sample: 1 189 partition_ids: 190 description: 191 - List of the partition id for the event endpoint. 192 type: list 193 returned: always 194 sample: ["0", "1"] 195 host_name: 196 description: 197 - Host of the IoT hub. 198 type: str 199 returned: always 200 sample: "testing.azure-devices.net" 201 ip_filters: 202 description: 203 - Configure rules for rejecting or accepting traffic from specific IPv4 addresses. 204 type: complex 205 returned: always 206 contains: 207 name: 208 description: 209 - Name of the filter. 210 type: str 211 returned: always 212 sample: filter 213 ip_mask: 214 description: 215 - A string that contains the IP address range in CIDR notation for the rule. 216 type: str 217 returned: always 218 sample: 40.54.7.3 219 action: 220 description: 221 - The desired action for requests captured by this rule. 222 type: str 223 returned: always 224 sample: Reject 225 routing_endpoints: 226 description: 227 - Custom endpoints. 228 type: complex 229 returned: always 230 contains: 231 event_hubs: 232 description: 233 - List of custom endpoints of event hubs. 234 type: complex 235 returned: always 236 contains: 237 name: 238 description: 239 - Name of the custom endpoint. 240 type: str 241 returned: always 242 sample: foo 243 resource_group: 244 description: 245 - Resource group of the endpoint. 246 type: str 247 returned: always 248 sample: bar 249 subscription: 250 description: 251 - Subscription ID of the endpoint. 252 type: str 253 returned: always 254 sample: "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" 255 connection_string: 256 description: 257 - Connection string of the custom endpoint. 258 type: str 259 returned: always 260 sample: "Endpoint=sb://quux.servicebus.windows.net:5671/;SharedAccessKeyName=qux;SharedAccessKey=****;EntityPath=foo" 261 service_bus_queues: 262 description: 263 - List of custom endpoints of service bus queue. 264 type: complex 265 returned: always 266 contains: 267 name: 268 description: 269 - Name of the custom endpoint. 270 type: str 271 returned: always 272 sample: foo 273 resource_group: 274 description: 275 - Resource group of the endpoint. 276 type: str 277 returned: always 278 sample: bar 279 subscription: 280 description: 281 - Subscription ID of the endpoint. 282 type: str 283 returned: always 284 sample: "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" 285 connection_string: 286 description: 287 - Connection string of the custom endpoint. 288 type: str 289 returned: always 290 sample: "Endpoint=sb://quux.servicebus.windows.net:5671/;SharedAccessKeyName=qux;SharedAccessKey=****;EntityPath=foo" 291 service_bus_topics: 292 description: 293 - List of custom endpoints of service bus topic. 294 type: complex 295 returned: always 296 contains: 297 name: 298 description: 299 - Name of the custom endpoint. 300 type: str 301 returned: always 302 sample: foo 303 resource_group: 304 description: 305 - Resource group of the endpoint. 306 type: str 307 returned: always 308 sample: bar 309 subscription: 310 description: 311 - Subscription ID of the endpoint. 312 type: str 313 returned: always 314 sample: "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" 315 connection_string: 316 description: 317 - Connection string of the custom endpoint. 318 type: str 319 returned: always 320 sample: "Endpoint=sb://quux.servicebus.windows.net:5671/;SharedAccessKeyName=qux;SharedAccessKey=****;EntityPath=foo" 321 storage_containers: 322 description: 323 - List of custom endpoints of storage. 324 type: complex 325 returned: always 326 contains: 327 name: 328 description: 329 - Name of the custom endpoint. 330 type: str 331 returned: always 332 sample: foo 333 resource_group: 334 description: 335 - Resource group of the endpoint. 336 type: str 337 returned: always 338 sample: bar 339 subscription: 340 description: 341 - Subscription ID of the endpoint. 342 type: str 343 returned: always 344 sample: "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" 345 connection_string: 346 description: 347 - Connection string of the custom endpoint. 348 type: str 349 returned: always 350 sample: "Endpoint=sb://quux.servicebus.windows.net:5671/;SharedAccessKeyName=qux;SharedAccessKey=****;EntityPath=foo" 351 routes: 352 description: 353 - Route device-to-cloud messages to service-facing endpoints. 354 type: complex 355 returned: always 356 contains: 357 name: 358 description: 359 - Name of the route. 360 type: str 361 returned: always 362 sample: route1 363 source: 364 description: 365 - The origin of the data stream to be acted upon. 366 type: str 367 returned: always 368 sample: device_messages 369 enabled: 370 description: 371 - Whether to enable the route. 372 type: bool 373 returned: always 374 sample: true 375 endpoint_name: 376 description: 377 - The name of the endpoint in I(routing_endpoints) where IoT Hub sends messages that match the query. 378 type: str 379 returned: always 380 sample: foo 381 condition: 382 description: 383 - "The query expression for the routing query that is run against the message application properties, 384 system properties, message body, device twin tags, and device twin properties to determine if it is a match for the endpoint." 385 - "For more information about constructing a query, 386 see U(https://docs.microsoft.com/en-us/azure/iot-hub/iot-hub-devguide-routing-query-syntax)" 387 type: bool 388 returned: always 389 sample: "true" 390 tags: 391 description: 392 - Limit results by providing a list of tags. Format tags as 'key' or 'key:value'. 393 type: dict 394 returned: always 395 sample: { 'key1': 'value1' } 396''' 397 398from ansible.module_utils.azure_rm_common import AzureRMModuleBase 399from ansible.module_utils.common.dict_transformations import _camel_to_snake 400 401try: 402 from msrestazure.azure_exceptions import CloudError 403 from msrestazure.tools import parse_resource_id 404 from azure.common import AzureHttpError 405except Exception: 406 # handled in azure_rm_common 407 pass 408 409 410class AzureRMIoTHubFacts(AzureRMModuleBase): 411 """Utility class to get IoT Hub facts""" 412 413 def __init__(self): 414 415 self.module_args = dict( 416 name=dict(type='str'), 417 resource_group=dict(type='str'), 418 tags=dict(type='list'), 419 show_stats=dict(type='bool'), 420 show_quota_metrics=dict(type='bool'), 421 show_endpoint_health=dict(type='bool'), 422 list_keys=dict(type='bool'), 423 test_route_message=dict(type='str'), 424 list_consumer_groups=dict(type='bool') 425 ) 426 427 self.results = dict( 428 changed=False, 429 azure_iothubs=[] 430 ) 431 432 self.name = None 433 self.resource_group = None 434 self.tags = None 435 self.show_stats = None 436 self.show_quota_metrics = None 437 self.show_endpoint_health = None 438 self.list_keys = None 439 self.test_route_message = None 440 self.list_consumer_groups = None 441 442 super(AzureRMIoTHubFacts, self).__init__( 443 derived_arg_spec=self.module_args, 444 supports_tags=False, 445 facts_module=True 446 ) 447 448 def exec_module(self, **kwargs): 449 450 for key in self.module_args: 451 setattr(self, key, kwargs[key]) 452 453 response = [] 454 if self.name: 455 response = self.get_item() 456 elif self.resource_group: 457 response = self.list_by_resource_group() 458 else: 459 response = self.list_all() 460 self.results['iothubs'] = [self.to_dict(x) for x in response if self.has_tags(x.tags, self.tags)] 461 return self.results 462 463 def get_item(self): 464 """Get a single IoT Hub""" 465 466 self.log('Get properties for {0}'.format(self.name)) 467 468 item = None 469 470 try: 471 item = self.IoThub_client.iot_hub_resource.get(self.resource_group, self.name) 472 return [item] 473 except Exception as exc: 474 self.fail('Error when getting IoT Hub {0}: {1}'.format(self.name, exc.message or str(exc))) 475 476 def list_all(self): 477 """Get all IoT Hubs""" 478 479 self.log('List all IoT Hubs') 480 481 try: 482 return self.IoThub_client.iot_hub_resource.list_by_subscription() 483 except Exception as exc: 484 self.fail('Failed to list all IoT Hubs - {0}'.format(str(exc))) 485 486 def list_by_resource_group(self): 487 try: 488 return self.IoThub_client.iot_hub_resource.list(self.resource_group) 489 except Exception as exc: 490 self.fail('Failed to list IoT Hub in resource group {0} - {1}'.format(self.resource_group, exc.message or str(exc))) 491 492 def show_hub_stats(self, resource_group, name): 493 try: 494 return self.IoThub_client.iot_hub_resource.get_stats(resource_group, name).as_dict() 495 except Exception as exc: 496 self.fail('Failed to getting statistics for IoT Hub {0}/{1}: {2}'.format(resource_group, name, str(exc))) 497 498 def show_hub_quota_metrics(self, resource_group, name): 499 result = [] 500 try: 501 resp = self.IoThub_client.iot_hub_resource.get_quota_metrics(resource_group, name) 502 while True: 503 result.append(resp.next().as_dict()) 504 except StopIteration: 505 pass 506 except Exception as exc: 507 self.fail('Failed to getting quota metrics for IoT Hub {0}/{1}: {2}'.format(resource_group, name, str(exc))) 508 return result 509 510 def show_hub_endpoint_health(self, resource_group, name): 511 result = [] 512 try: 513 resp = self.IoThub_client.iot_hub_resource.get_endpoint_health(resource_group, name) 514 while True: 515 result.append(resp.next().as_dict()) 516 except StopIteration: 517 pass 518 except Exception as exc: 519 self.fail('Failed to getting health for IoT Hub {0}/{1} routing endpoint: {2}'.format(resource_group, name, str(exc))) 520 return result 521 522 def test_all_routes(self, resource_group, name): 523 try: 524 return self.IoThub_client.iot_hub_resource.test_all_routes(self.test_route_message, resource_group, name).routes.as_dict() 525 except Exception as exc: 526 self.fail('Failed to getting statistics for IoT Hub {0}/{1}: {2}'.format(resource_group, name, str(exc))) 527 528 def list_hub_keys(self, resource_group, name): 529 result = [] 530 try: 531 resp = self.IoThub_client.iot_hub_resource.list_keys(resource_group, name) 532 while True: 533 result.append(resp.next().as_dict()) 534 except StopIteration: 535 pass 536 except Exception as exc: 537 self.fail('Failed to getting health for IoT Hub {0}/{1} routing endpoint: {2}'.format(resource_group, name, str(exc))) 538 return result 539 540 def list_event_hub_consumer_groups(self, resource_group, name, event_hub_endpoint='events'): 541 result = [] 542 try: 543 resp = self.IoThub_client.iot_hub_resource.list_event_hub_consumer_groups(resource_group, name, event_hub_endpoint) 544 while True: 545 cg = resp.next() 546 result.append(dict( 547 id=cg.id, 548 name=cg.name 549 )) 550 except StopIteration: 551 pass 552 except Exception as exc: 553 self.fail('Failed to listing consumer group for IoT Hub {0}/{1} routing endpoint: {2}'.format(resource_group, name, str(exc))) 554 return result 555 556 def route_to_dict(self, route): 557 return dict( 558 name=route.name, 559 source=_camel_to_snake(route.source), 560 endpoint_name=route.endpoint_names[0], 561 enabled=route.is_enabled, 562 condition=route.condition 563 ) 564 565 def instance_dict_to_dict(self, instance_dict): 566 result = dict() 567 for key in instance_dict.keys(): 568 result[key] = instance_dict[key].as_dict() 569 return result 570 571 def to_dict(self, hub): 572 result = dict() 573 properties = hub.properties 574 result['id'] = hub.id 575 result['name'] = hub.name 576 result['resource_group'] = parse_resource_id(hub.id).get('resource_group') 577 result['location'] = hub.location 578 result['tags'] = hub.tags 579 result['unit'] = hub.sku.capacity 580 result['sku'] = hub.sku.name.lower() 581 result['cloud_to_device'] = dict( 582 max_delivery_count=properties.cloud_to_device.feedback.max_delivery_count, 583 ttl_as_iso8601=str(properties.cloud_to_device.feedback.ttl_as_iso8601) 584 ) 585 result['enable_file_upload_notifications'] = properties.enable_file_upload_notifications 586 result['event_hub_endpoints'] = self.instance_dict_to_dict(properties.event_hub_endpoints) 587 result['host_name'] = properties.host_name 588 result['ip_filters'] = [x.as_dict() for x in properties.ip_filter_rules] 589 result['routing_endpoints'] = properties.routing.endpoints.as_dict() 590 result['routes'] = [self.route_to_dict(x) for x in properties.routing.routes] 591 result['fallback_route'] = self.route_to_dict(properties.routing.fallback_route) 592 result['status'] = properties.state 593 result['storage_endpoints'] = self.instance_dict_to_dict(properties.storage_endpoints) 594 595 # network overhead part 596 if self.show_stats: 597 result['statistics'] = self.show_hub_stats(result['resource_group'], hub.name) 598 if self.show_quota_metrics: 599 result['quota_metrics'] = self.show_hub_quota_metrics(result['resource_group'], hub.name) 600 if self.show_endpoint_health: 601 result['endpoint_health'] = self.show_hub_endpoint_health(result['resource_group'], hub.name) 602 if self.list_keys: 603 result['keys'] = self.list_hub_keys(result['resource_group'], hub.name) 604 if self.test_route_message: 605 result['test_route_result'] = self.test_all_routes(result['resource_group'], hub.name) 606 if self.list_consumer_groups: 607 result['consumer_groups'] = self.list_event_hub_consumer_groups(result['resource_group'], hub.name) 608 return result 609 610 611def main(): 612 """Main module execution code path""" 613 614 AzureRMIoTHubFacts() 615 616 617if __name__ == '__main__': 618 main() 619