Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

azure_rm_storageblob - Add support standard_blob_tier #1764

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions meta/runtime.yml
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,7 @@ action_groups:
- azure.azcollection.azure_rm_storageaccountmanagementpolicy
- azure.azcollection.azure_rm_storageaccountmanagementpolicy_info
- azure.azcollection.azure_rm_storageblob
- azure.azcollection.azure_rm_storageblob_info
- azure.azcollection.azure_rm_storageshare
- azure.azcollection.azure_rm_storageshare_info
- azure.azcollection.azure_rm_subnet
Expand Down
41 changes: 39 additions & 2 deletions plugins/modules/azure_rm_storageblob.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,16 @@
description:
- Base directory in container when upload batch of files.
type: path
standard_blob_tier:
description:
- Specifies the blob tier to set the blob to.
- This is only applicable for block blobs on standard storage accounts.
type: str
choices:
- Archive
- Cool
- Cold
- Hot
state:
description:
- State of a container or blob.
Expand Down Expand Up @@ -202,7 +212,8 @@
"last_modified": "09-Mar-2016 22:08:25 +0000",
"name": "graylog.png",
"tags": {},
"type": "BlockBlob"
"type": "BlockBlob",
'standard_blob_tier': 'Hot'
}
container:
description:
Expand All @@ -220,7 +231,7 @@
import mimetypes

try:
from azure.storage.blob._models import BlobType, ContentSettings
from azure.storage.blob._models import BlobType, ContentSettings, StandardBlobTier
from azure.core.exceptions import ResourceNotFoundError
except ImportError:
# This is handled in azure_rm_common
Expand All @@ -244,6 +255,7 @@ def __init__(self):
storage_account_name=dict(required=True, type='str', aliases=['account_name', 'storage_account']),
blob=dict(type='str', aliases=['blob_name']),
blob_type=dict(type='str', default='block', choices=['block', 'page']),
standard_blob_tier=dict(type='str', choices=['Archive', 'Cool', 'Cold', 'Hot']),
container=dict(required=True, type='str', aliases=['container_name']),
dest=dict(type='path', aliases=['destination']),
force=dict(type='bool', default=False),
Expand All @@ -269,6 +281,7 @@ def __init__(self):
self.blob = None
self.blob_obj = None
self.blob_type = None
self.standard_blob_tier = None
self.container = None
self.container_obj = None
self.dest = None
Expand Down Expand Up @@ -337,6 +350,10 @@ def exec_module(self, **kwargs):
if self.blob_content_settings_differ():
self.update_blob_content_settings()

if self.standard_blob_tier is not None and self.blob_obj.get('standard_blob_tier') is not None and \
self.blob_obj['standard_blob_tier'] != self.standard_blob_tier:
self.update_blob_tier()

elif self.state == 'absent':
if self.container_obj and not self.blob:
# Delete container
Expand Down Expand Up @@ -416,6 +433,7 @@ def _guess_content_type(file_path, original):
blob_type=self.get_blob_type(self.blob_type),
metadata=self.tags,
content_settings=_guess_content_type(src, content_settings),
standard_blob_tier=self.get_blob_tier(self.standard_blob_tier),
overwrite=self.force)
except Exception as exc:
self.fail("Error creating blob {0} - {1}".format(src, str(exc)))
Expand All @@ -424,6 +442,18 @@ def _guess_content_type(file_path, original):
self.results['changed'] = True
self.results['container'] = self.container_obj

def get_blob_tier(self, blob_tier):
if blob_tier == "archive":
return StandardBlobTier.Archive
elif blob_tier == "cool":
return StandardBlobTier.Cool
elif blob_tier == "hot":
return StandardBlobTier.Hot
elif blob_tier == "cold":
return StandardBlobTier.Cold
else:
return None

def get_blob_type(self, blob_type):
if blob_type == "block":
return BlobType.BlockBlob
Expand Down Expand Up @@ -462,6 +492,7 @@ def get_blob(self):
tags=blob["metadata"],
last_modified=blob["last_modified"].strftime('%d-%b-%Y %H:%M:%S %z'),
type=blob["blob_type"],
standard_blob_tier=blob.get('blob_tier'),
content_length=blob["size"],
content_settings=dict(
content_type=blob["content_settings"]["content_type"],
Expand Down Expand Up @@ -513,6 +544,7 @@ def upload_blob(self):
blob_type=self.get_blob_type(self.blob_type),
metadata=self.tags,
content_settings=content_settings,
standard_blob_tier=self.get_blob_tier(self.standard_blob_tier),
overwrite=self.force)
except Exception as exc:
self.fail("Error creating blob {0} - {1}".format(self.blob, str(exc)))
Expand Down Expand Up @@ -636,6 +668,11 @@ def update_blob_tags(self, tags):
self.results['container'] = self.container_obj
self.results['blob'] = self.blob_obj

def update_blob_tier(self):
client = self.blob_service_client.get_blob_client(container=self.container, blob=self.blob)
client.set_standard_blob_tier(standard_blob_tier=self.standard_blob_tier)
self.results['changed'] = True

def blob_content_settings_differ(self):
if self.content_type or self.content_encoding or self.content_language or self.content_disposition or \
self.cache_control or self.content_md5:
Expand Down
236 changes: 236 additions & 0 deletions plugins/modules/azure_rm_storageblob_info.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
#!/usr/bin/python
#
# Copyright (c) 2024 xuzhang3 (@xuzhang3), Fred-sun (@Fred-sun)
#
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

from __future__ import absolute_import, division, print_function
__metaclass__ = type


DOCUMENTATION = '''
---
module: azure_rm_storageblob_info
short_description: Get or list the containers blob facts
version_added: "3.0.0"
description:
- Get or list the blobs under the specified container.
options:
auth_mode:
description:
- The mode in which to run the command. C(login) mode will directly use your login credentials for the authentication.
- The legacy C(key) mode will attempt to query for an account key if no authentication parameters for the account are provided.
- Can also be set via the environment variable C(AZURE_STORAGE_AUTH_MODE).
default: key
type: str
choices:
- key
- login
storage_account_name:
description:
- Name of the storage account to use.
required: true
type: str
aliases:
- account_name
- storage_account
blob_name:
description:
- Name of a blob object within the container.
aliases:
- blob
type: str
container_name:
description:
- Name of a blob container within the storage account.
required: true
type: str
aliases:
- container
resource_group:
description:
- Name of the resource group to use.
required: true
type: str
aliases:
- resource_group_name
name_starts_with:
description:
- Filters the results to return only blobs whose names begin with the specified prefix.
type: str
include:
description:
- Specifies one or more additional datasets to include in the response.
type: list
elements: str
choices:
- snapshots
- metadata
- uncommittedblobs
- copy
- deleted
- deletedwithversions
- tags
- versions
- immutabilitypolicy
- legalhold
extends_documentation_fragment:
- azure.azcollection.azure

author:
- xuzhang3 (@xuzhang3)
- Fred-sun (@Fred-sun)

'''

EXAMPLES = '''
- name: Get the blob facts by name
azure_rm_storageblob_info:
resource_group: "{{ resource_group }}"
account_name: "{{ storage_account }}"
container_name: my-blobs
blob_name: blobname01

- name: List the blob facts in specify container
azure_rm_storageblob_info:
resource_group: "{{ resource_group }}"
account_name: "{{ storage_account }}"
container_name: my-blobs
'''

RETURN = '''
blob:
description:
- Facts about the current state of the blob.
returned: when a blob is operated on
type: dict
sample: {
"content_length": 136532,
"content_settings": {
"cache_control": null,
"content_disposition": null,
"content_encoding": null,
"content_language": null,
"content_md5": null,
"content_type": "application/image"
},
"last_modified": "20-11-2024 22:08:25 +0000",
"name": "graylog.png",
'metadata': {'key1': 'value1'},
"standard_blob_tier": "Hot",
"tags": {},
"type": "BlockBlob"
}
'''


try:
from azure.core.exceptions import ResourceNotFoundError
except ImportError:
# This is handled in azure_rm_common
pass

from ansible_collections.azure.azcollection.plugins.module_utils.azure_rm_common import AzureRMModuleBase
from ansible.module_utils.basic import env_fallback


class AzureRMStorageBlobInfo(AzureRMModuleBase):

def __init__(self):

self.module_arg_spec = dict(
auth_mode=dict(
type='str',
choices=['key', 'login'],
fallback=(env_fallback, ['AZURE_STORAGE_AUTH_MODE']),
default="key"
),
storage_account_name=dict(required=True, type='str', aliases=['account_name', 'storage_account']),
blob_name=dict(type='str', aliases=['blob']),
container_name=dict(required=True, type='str', aliases=['container']),
resource_group=dict(required=True, type='str', aliases=['resource_group_name']),
name_starts_with=dict(type='str'),
include=dict(
type='list',
elements='str',
choices=['snapshots', 'metadata', 'uncommittedblobs', 'copy', 'deleted',
'deletedwithversions', 'tags', 'versions', 'immutabilitypolicy', 'legalhold']
),
)

self.blob_service_client = None
self.storage_account_name = None
self.blob_name = None
self.container_name = None
self.resource_group = None
self.name_starts_with = None
self.include = None
self.results = dict(
changed=False,
blob=dict()
)

super(AzureRMStorageBlobInfo, self).__init__(derived_arg_spec=self.module_arg_spec,
supports_check_mode=True,
supports_tags=False)

def exec_module(self, **kwargs):

for key in list(self.module_arg_spec.keys()):
setattr(self, key, kwargs[key])

self.blob_service_client = self.get_blob_service_client(self.resource_group, self.storage_account_name, self.auth_mode)
if self.blob_name:
response = self.get_blob()
else:
response = self.list_blob()
self.results['blob'] = response
return self.results

def get_blob(self):
response = None
if self.blob_name:
try:
response = self.blob_service_client.get_blob_client(container=self.container_name, blob=self.blob_name).get_blob_properties()
except ResourceNotFoundError:
pass
return self.format_blob(response) if response else None

def list_blob(self):
response = []
try:
client = self.blob_service_client.get_container_client(container=self.container_name)
blobs = client.list_blobs(name_starts_with=self.name_starts_with, include=self.include)
for blob in blobs:
response.append(self.format_blob(blob))
except Exception as exc:
self.fail("Error list container blob {0} - {1}".format(self.container_name, str(exc)))
return response

def format_blob(self, blob):
result = dict(
name=blob["name"],
tags=blob["metadata"],
last_modified=blob["last_modified"].strftime('%d-%b-%Y %H:%M:%S %z'),
type=blob["blob_type"],
standard_blob_tier=blob.get('blob_tier'),
metadata=blob.get('metadata'),
content_length=blob["size"],
content_settings=dict(
content_type=blob["content_settings"]["content_type"],
content_encoding=blob["content_settings"]["content_encoding"],
content_language=blob["content_settings"]["content_language"],
content_disposition=blob["content_settings"]["content_disposition"],
cache_control=blob["content_settings"]["cache_control"],
content_md5=blob["content_settings"]["content_md5"].hex() if blob["content_settings"]["content_md5"] else None,
)
)
return result


def main():
AzureRMStorageBlobInfo()


if __name__ == '__main__':
main()
Loading