1"""
2.. versionadded:: 2015.8.0
3
4Utilities for accessing storage container blobs on Azure
5"""
6
7
8import logging
9
10from salt.exceptions import SaltSystemExit
11
12HAS_LIBS = False
13try:
14    import azure
15
16    HAS_LIBS = True
17except ImportError:
18    pass
19
20
21log = logging.getLogger(__name__)
22
23
24def get_storage_conn(storage_account=None, storage_key=None, opts=None):
25    """
26    .. versionadded:: 2015.8.0
27
28    Return a storage_conn object for the storage account
29    """
30    if opts is None:
31        opts = {}
32
33    if not storage_account:
34        storage_account = opts.get("storage_account", None)
35    if not storage_key:
36        storage_key = opts.get("storage_key", None)
37
38    return azure.storage.BlobService(storage_account, storage_key)
39
40
41def list_blobs(storage_conn=None, **kwargs):
42    """
43    .. versionadded:: 2015.8.0
44
45    List blobs associated with the container
46    """
47    if not storage_conn:
48        storage_conn = get_storage_conn(opts=kwargs)
49
50    if "container" not in kwargs:
51        raise SaltSystemExit(
52            code=42, msg='An storage container name must be specified as "container"'
53        )
54
55    data = storage_conn.list_blobs(
56        container_name=kwargs["container"],
57        prefix=kwargs.get("prefix", None),
58        marker=kwargs.get("marker", None),
59        maxresults=kwargs.get("maxresults", None),
60        include=kwargs.get("include", None),
61        delimiter=kwargs.get("delimiter", None),
62    )
63
64    ret = {}
65    for item in data.blobs:
66        ret[item.name] = object_to_dict(item)
67    return ret
68
69
70def put_blob(storage_conn=None, **kwargs):
71    """
72    .. versionadded:: 2015.8.0
73
74    Upload a blob
75    """
76    if not storage_conn:
77        storage_conn = get_storage_conn(opts=kwargs)
78
79    if "container" not in kwargs:
80        raise SaltSystemExit(
81            code=42, msg='The blob container name must be specified as "container"'
82        )
83
84    if "name" not in kwargs:
85        raise SaltSystemExit(code=42, msg='The blob name must be specified as "name"')
86
87    if "blob_path" not in kwargs and "blob_content" not in kwargs:
88        raise SaltSystemExit(
89            code=42,
90            msg=(
91                'Either a path to a file needs to be passed in as "blob_path" '
92                'or the contents of a blob as "blob_content."'
93            ),
94        )
95
96    blob_kwargs = {
97        "container_name": kwargs["container"],
98        "blob_name": kwargs["name"],
99        "cache_control": kwargs.get("cache_control", None),
100        "content_language": kwargs.get("content_language", None),
101        "content_md5": kwargs.get("content_md5", None),
102        "x_ms_blob_content_type": kwargs.get("blob_content_type", None),
103        "x_ms_blob_content_encoding": kwargs.get("blob_content_encoding", None),
104        "x_ms_blob_content_language": kwargs.get("blob_content_language", None),
105        "x_ms_blob_content_md5": kwargs.get("blob_content_md5", None),
106        "x_ms_blob_cache_control": kwargs.get("blob_cache_control", None),
107        "x_ms_meta_name_values": kwargs.get("meta_name_values", None),
108        "x_ms_lease_id": kwargs.get("lease_id", None),
109    }
110    if "blob_path" in kwargs:
111        data = storage_conn.put_block_blob_from_path(
112            file_path=kwargs["blob_path"], **blob_kwargs
113        )
114    elif "blob_content" in kwargs:
115        data = storage_conn.put_block_blob_from_bytes(
116            blob=kwargs["blob_content"], **blob_kwargs
117        )
118
119    return data
120
121
122def get_blob(storage_conn=None, **kwargs):
123    """
124    .. versionadded:: 2015.8.0
125
126    Download a blob
127    """
128    if not storage_conn:
129        storage_conn = get_storage_conn(opts=kwargs)
130
131    if "container" not in kwargs:
132        raise SaltSystemExit(
133            code=42, msg='The blob container name must be specified as "container"'
134        )
135
136    if "name" not in kwargs:
137        raise SaltSystemExit(code=42, msg='The blob name must be specified as "name"')
138
139    if "local_path" not in kwargs and "return_content" not in kwargs:
140        raise SaltSystemExit(
141            code=42,
142            msg=(
143                'Either a local path needs to be passed in as "local_path", '
144                'or "return_content" to return the blob contents directly'
145            ),
146        )
147
148    blob_kwargs = {
149        "container_name": kwargs["container"],
150        "blob_name": kwargs["name"],
151        "snapshot": kwargs.get("snapshot", None),
152        "x_ms_lease_id": kwargs.get("lease_id", None),
153        "progress_callback": kwargs.get("progress_callback", None),
154        "max_connections": kwargs.get("max_connections", 1),
155        "max_retries": kwargs.get("max_retries", 5),
156        "retry_wait": kwargs.get("retry_wait", 1),
157    }
158
159    if "local_path" in kwargs:
160        data = storage_conn.get_blob_to_path(
161            file_path=kwargs["local_path"],
162            open_mode=kwargs.get("open_mode", "wb"),
163            **blob_kwargs
164        )
165    elif "return_content" in kwargs:
166        data = storage_conn.get_blob_to_bytes(**blob_kwargs)
167
168    return data
169
170
171def object_to_dict(obj):
172    """
173    .. versionadded:: 2015.8.0
174
175    Convert an object to a dictionary
176    """
177    if isinstance(obj, list) or isinstance(obj, tuple):
178        ret = []
179        for item in obj:
180            ret.append(object_to_dict(item))
181    elif hasattr(obj, "__dict__"):
182        ret = {}
183        for item in obj.__dict__:
184            if item.startswith("_"):
185                continue
186            ret[item] = object_to_dict(obj.__dict__[item])
187    else:
188        ret = obj
189    return ret
190