1#!/usr/bin/python
2
3# Copyright: (c) 2018, Loic BLOT (@nerzhul) <loic.blot@unix-experience.fr>
4# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
5
6# This module is sponsored by E.T.A.I. (www.etai.fr)
7
8ANSIBLE_METADATA = {'metadata_version': '1.1',
9                    'status': ['preview'],
10                    'supported_by': 'community'}
11
12DOCUMENTATION = '''
13---
14module: aws_sgw_info
15short_description: Fetch AWS Storage Gateway information
16description:
17    - Fetch AWS Storage Gateway information
18    - This module was called C(aws_sgw_facts) before Ansible 2.9. The usage did not change.
19version_added: "2.6"
20requirements: [ boto3 ]
21author: Loic Blot (@nerzhul) <loic.blot@unix-experience.fr>
22options:
23  gather_local_disks:
24    description:
25      - Gather local disks attached to the storage gateway.
26    type: bool
27    required: false
28    default: true
29  gather_tapes:
30    description:
31      - Gather tape information for storage gateways in tape mode.
32    type: bool
33    required: false
34    default: true
35  gather_file_shares:
36    description:
37      - Gather file share information for storage gateways in s3 mode.
38    type: bool
39    required: false
40    default: true
41  gather_volumes:
42    description:
43      - Gather volume information for storage gateways in iSCSI (cached & stored) modes.
44    type: bool
45    required: false
46    default: true
47extends_documentation_fragment:
48    - aws
49    - ec2
50'''
51
52RETURN = '''
53gateways:
54  description: list of gateway objects
55  returned: always
56  type: complex
57  contains:
58    gateway_arn:
59      description: "Storage Gateway ARN"
60      returned: always
61      type: str
62      sample: "arn:aws:storagegateway:eu-west-1:367709993819:gateway/sgw-9999F888"
63    gateway_id:
64      description: "Storage Gateway ID"
65      returned: always
66      type: str
67      sample: "sgw-9999F888"
68    gateway_name:
69      description: "Storage Gateway friendly name"
70      returned: always
71      type: str
72      sample: "my-sgw-01"
73    gateway_operational_state:
74      description: "Storage Gateway operational state"
75      returned: always
76      type: str
77      sample: "ACTIVE"
78    gateway_type:
79      description: "Storage Gateway type"
80      returned: always
81      type: str
82      sample: "FILE_S3"
83    file_shares:
84      description: "Storage gateway file shares"
85      returned: when gateway_type == "FILE_S3"
86      type: complex
87      contains:
88        file_share_arn:
89          description: "File share ARN"
90          returned: always
91          type: str
92          sample: "arn:aws:storagegateway:eu-west-1:399805793479:share/share-AF999C88"
93        file_share_id:
94          description: "File share ID"
95          returned: always
96          type: str
97          sample: "share-AF999C88"
98        file_share_status:
99          description: "File share status"
100          returned: always
101          type: str
102          sample: "AVAILABLE"
103    tapes:
104        description: "Storage Gateway tapes"
105        returned: when gateway_type == "VTL"
106        type: complex
107        contains:
108          tape_arn:
109            description: "Tape ARN"
110            returned: always
111            type: str
112            sample: "arn:aws:storagegateway:eu-west-1:399805793479:tape/tape-AF999C88"
113          tape_barcode:
114            description: "Tape ARN"
115            returned: always
116            type: str
117            sample: "tape-AF999C88"
118          tape_size_in_bytes:
119            description: "Tape ARN"
120            returned: always
121            type: int
122            sample: 555887569
123          tape_status:
124            description: "Tape ARN"
125            returned: always
126            type: str
127            sample: "AVAILABLE"
128    local_disks:
129      description: "Storage gateway local disks"
130      returned: always
131      type: complex
132      contains:
133        disk_allocation_type:
134          description: "Disk allocation type"
135          returned: always
136          type: str
137          sample: "CACHE STORAGE"
138        disk_id:
139          description: "Disk ID on the system"
140          returned: always
141          type: str
142          sample: "pci-0000:00:1f.0"
143        disk_node:
144          description: "Disk parent block device"
145          returned: always
146          type: str
147          sample: "/dev/sdb"
148        disk_path:
149          description: "Disk path used for the cache"
150          returned: always
151          type: str
152          sample: "/dev/nvme1n1"
153        disk_size_in_bytes:
154          description: "Disk size in bytes"
155          returned: always
156          type: int
157          sample: 107374182400
158        disk_status:
159          description: "Disk status"
160          returned: always
161          type: str
162          sample: "present"
163'''
164
165EXAMPLES = '''
166# Note: These examples do not set authentication details, see the AWS Guide for details.
167
168- name: "Get AWS storage gateway information"
169  aws_sgw_info:
170
171- name: "Get AWS storage gateway information for region eu-west-3"
172  aws_sgw_info:
173    region: eu-west-3
174'''
175
176from ansible.module_utils.aws.core import AnsibleAWSModule
177from ansible.module_utils.ec2 import camel_dict_to_snake_dict
178
179try:
180    from botocore.exceptions import BotoCoreError, ClientError
181except ImportError:
182    pass  # caught by imported HAS_BOTO3
183
184
185class SGWInformationManager(object):
186    def __init__(self, client, module):
187        self.client = client
188        self.module = module
189        self.name = self.module.params.get('name')
190
191    def fetch(self):
192        gateways = self.list_gateways()
193        for gateway in gateways:
194            if self.module.params.get('gather_local_disks'):
195                self.list_local_disks(gateway)
196            # File share gateway
197            if gateway["gateway_type"] == "FILE_S3" and self.module.params.get('gather_file_shares'):
198                self.list_gateway_file_shares(gateway)
199            # Volume tape gateway
200            elif gateway["gateway_type"] == "VTL" and self.module.params.get('gather_tapes'):
201                self.list_gateway_vtl(gateway)
202            # iSCSI gateway
203            elif gateway["gateway_type"] in ["CACHED", "STORED"] and self.module.params.get('gather_volumes'):
204                self.list_gateway_volumes(gateway)
205
206        self.module.exit_json(gateways=gateways)
207
208    """
209    List all storage gateways for the AWS endpoint.
210    """
211    def list_gateways(self):
212        try:
213            paginator = self.client.get_paginator('list_gateways')
214            response = paginator.paginate(
215                PaginationConfig={
216                    'PageSize': 100,
217                }
218            ).build_full_result()
219
220            gateways = []
221            for gw in response["Gateways"]:
222                gateways.append(camel_dict_to_snake_dict(gw))
223
224            return gateways
225
226        except (BotoCoreError, ClientError) as e:
227            self.module.fail_json_aws(e, msg="Couldn't list storage gateways")
228
229    """
230    Read file share objects from AWS API response.
231    Drop the gateway_arn attribute from response, as it will be duplicate with parent object.
232    """
233    @staticmethod
234    def _read_gateway_fileshare_response(fileshares, aws_reponse):
235        for share in aws_reponse["FileShareInfoList"]:
236            share_obj = camel_dict_to_snake_dict(share)
237            if "gateway_arn" in share_obj:
238                del share_obj["gateway_arn"]
239            fileshares.append(share_obj)
240
241        return aws_reponse["NextMarker"] if "NextMarker" in aws_reponse else None
242
243    """
244    List file shares attached to AWS storage gateway when in S3 mode.
245    """
246    def list_gateway_file_shares(self, gateway):
247        try:
248            response = self.client.list_file_shares(
249                GatewayARN=gateway["gateway_arn"],
250                Limit=100
251            )
252
253            gateway["file_shares"] = []
254            marker = self._read_gateway_fileshare_response(gateway["file_shares"], response)
255
256            while marker is not None:
257                response = self.client.list_file_shares(
258                    GatewayARN=gateway["gateway_arn"],
259                    Marker=marker,
260                    Limit=100
261                )
262
263                marker = self._read_gateway_fileshare_response(gateway["file_shares"], response)
264        except (BotoCoreError, ClientError) as e:
265            self.module.fail_json_aws(e, msg="Couldn't list gateway file shares")
266
267    """
268    List storage gateway local disks
269    """
270    def list_local_disks(self, gateway):
271        try:
272            gateway['local_disks'] = [camel_dict_to_snake_dict(disk) for disk in
273                                      self.client.list_local_disks(GatewayARN=gateway["gateway_arn"])['Disks']]
274        except (BotoCoreError, ClientError) as e:
275            self.module.fail_json_aws(e, msg="Couldn't list storage gateway local disks")
276
277    """
278    Read tape objects from AWS API response.
279    Drop the gateway_arn attribute from response, as it will be duplicate with parent object.
280    """
281    @staticmethod
282    def _read_gateway_tape_response(tapes, aws_response):
283        for tape in aws_response["TapeInfos"]:
284            tape_obj = camel_dict_to_snake_dict(tape)
285            if "gateway_arn" in tape_obj:
286                del tape_obj["gateway_arn"]
287            tapes.append(tape_obj)
288
289        return aws_response["Marker"] if "Marker" in aws_response else None
290
291    """
292    List VTL & VTS attached to AWS storage gateway in VTL mode
293    """
294    def list_gateway_vtl(self, gateway):
295        try:
296            response = self.client.list_tapes(
297                Limit=100
298            )
299
300            gateway["tapes"] = []
301            marker = self._read_gateway_tape_response(gateway["tapes"], response)
302
303            while marker is not None:
304                response = self.client.list_tapes(
305                    Marker=marker,
306                    Limit=100
307                )
308
309                marker = self._read_gateway_tape_response(gateway["tapes"], response)
310        except (BotoCoreError, ClientError) as e:
311            self.module.fail_json_aws(e, msg="Couldn't list storage gateway tapes")
312
313    """
314    List volumes attached to AWS storage gateway in CACHED or STORAGE mode
315    """
316    def list_gateway_volumes(self, gateway):
317        try:
318            paginator = self.client.get_paginator('list_volumes')
319            response = paginator.paginate(
320                GatewayARN=gateway["gateway_arn"],
321                PaginationConfig={
322                    'PageSize': 100,
323                }
324            ).build_full_result()
325
326            gateway["volumes"] = []
327            for volume in response["VolumeInfos"]:
328                volume_obj = camel_dict_to_snake_dict(volume)
329                if "gateway_arn" in volume_obj:
330                    del volume_obj["gateway_arn"]
331                if "gateway_id" in volume_obj:
332                    del volume_obj["gateway_id"]
333
334                gateway["volumes"].append(volume_obj)
335        except (BotoCoreError, ClientError) as e:
336            self.module.fail_json_aws(e, msg="Couldn't list storage gateway volumes")
337
338
339def main():
340    argument_spec = dict(
341        gather_local_disks=dict(type='bool', default=True),
342        gather_tapes=dict(type='bool', default=True),
343        gather_file_shares=dict(type='bool', default=True),
344        gather_volumes=dict(type='bool', default=True)
345    )
346
347    module = AnsibleAWSModule(argument_spec=argument_spec)
348    if module._name == 'aws_sgw_facts':
349        module.deprecate("The 'aws_sgw_facts' module has been renamed to 'aws_sgw_info'", version='2.13')
350    client = module.client('storagegateway')
351
352    if client is None:  # this should never happen
353        module.fail_json(msg='Unknown error, failed to create storagegateway client, no information from boto.')
354
355    SGWInformationManager(client, module).fetch()
356
357
358if __name__ == '__main__':
359    main()
360