Update azure package to 0.9.0

This commit is contained in:
Matthew Jones
2015-01-29 12:13:53 -05:00
parent 18e7541f3b
commit d2c46d015b
24 changed files with 15414 additions and 12009 deletions

View File

@@ -5,6 +5,7 @@ amqp==1.4.5 (amqp/*)
ansi2html==1.0.6 (ansi2html/*) ansi2html==1.0.6 (ansi2html/*)
anyjson==0.3.3 (anyjson/*) anyjson==0.3.3 (anyjson/*)
argparse==1.2.1 (argparse.py, needed for Python 2.6 support) argparse==1.2.1 (argparse.py, needed for Python 2.6 support)
azure==0.9.0 (azure/*)
Babel==1.3 (babel/*, excluded bin/pybabel) Babel==1.3 (babel/*, excluded bin/pybabel)
billiard==3.3.0.16 (billiard/*, funtests/*, excluded _billiard.so) billiard==3.3.0.16 (billiard/*, funtests/*, excluded _billiard.so)
boto==2.34.0 (boto/*, excluded bin/asadmin, bin/bundle_image, bin/cfadmin, boto==2.34.0 (boto/*, excluded bin/asadmin, bin/bundle_image, bin/cfadmin,

View File

@@ -14,6 +14,8 @@
#-------------------------------------------------------------------------- #--------------------------------------------------------------------------
import ast import ast
import base64 import base64
import hashlib
import hmac
import sys import sys
import types import types
import warnings import warnings
@@ -34,7 +36,7 @@ from xml.sax.saxutils import escape as xml_escape
# constants # constants
__author__ = 'Microsoft Corp. <ptvshelp@microsoft.com>' __author__ = 'Microsoft Corp. <ptvshelp@microsoft.com>'
__version__ = '0.8.1' __version__ = '0.9.0'
# Live ServiceClient URLs # Live ServiceClient URLs
BLOB_SERVICE_HOST_BASE = '.blob.core.windows.net' BLOB_SERVICE_HOST_BASE = '.blob.core.windows.net'
@@ -103,10 +105,9 @@ class WindowsAzureData(object):
It is only used to check whether it is instance or not. ''' It is only used to check whether it is instance or not. '''
pass pass
class WindowsAzureError(Exception): class WindowsAzureError(Exception):
''' WindowsAzure Excpetion base class. ''' ''' WindowsAzure Exception base class. '''
def __init__(self, message): def __init__(self, message):
super(WindowsAzureError, self).__init__(message) super(WindowsAzureError, self).__init__(message)
@@ -188,25 +189,38 @@ def _get_readable_id(id_name, id_prefix_to_skip):
return id_name return id_name
def _get_entry_properties_from_node(entry, include_id, id_prefix_to_skip=None, use_title_as_id=False):
''' get properties from entry xml '''
properties = {}
etag = entry.getAttributeNS(METADATA_NS, 'etag')
if etag:
properties['etag'] = etag
for updated in _get_child_nodes(entry, 'updated'):
properties['updated'] = updated.firstChild.nodeValue
for name in _get_children_from_path(entry, 'author', 'name'):
if name.firstChild is not None:
properties['author'] = name.firstChild.nodeValue
if include_id:
if use_title_as_id:
for title in _get_child_nodes(entry, 'title'):
properties['name'] = title.firstChild.nodeValue
else:
for id in _get_child_nodes(entry, 'id'):
properties['name'] = _get_readable_id(
id.firstChild.nodeValue, id_prefix_to_skip)
return properties
def _get_entry_properties(xmlstr, include_id, id_prefix_to_skip=None): def _get_entry_properties(xmlstr, include_id, id_prefix_to_skip=None):
''' get properties from entry xml ''' ''' get properties from entry xml '''
xmldoc = minidom.parseString(xmlstr) xmldoc = minidom.parseString(xmlstr)
properties = {} properties = {}
for entry in _get_child_nodes(xmldoc, 'entry'): for entry in _get_child_nodes(xmldoc, 'entry'):
etag = entry.getAttributeNS(METADATA_NS, 'etag') properties.update(_get_entry_properties_from_node(entry, include_id, id_prefix_to_skip))
if etag:
properties['etag'] = etag
for updated in _get_child_nodes(entry, 'updated'):
properties['updated'] = updated.firstChild.nodeValue
for name in _get_children_from_path(entry, 'author', 'name'):
if name.firstChild is not None:
properties['author'] = name.firstChild.nodeValue
if include_id:
for id in _get_child_nodes(entry, 'id'):
properties['name'] = _get_readable_id(
id.firstChild.nodeValue, id_prefix_to_skip)
return properties return properties
@@ -284,6 +298,18 @@ _KNOWN_SERIALIZATION_XFORMS = {
'os': 'OS', 'os': 'OS',
'persistent_vm_downtime_info': 'PersistentVMDowntimeInfo', 'persistent_vm_downtime_info': 'PersistentVMDowntimeInfo',
'copy_id': 'CopyId', 'copy_id': 'CopyId',
'os_state': 'OSState',
'vm_image': 'VMImage',
'vm_images': 'VMImages',
'os_disk_configuration': 'OSDiskConfiguration',
'public_ips': 'PublicIPs',
'public_ip': 'PublicIP',
'supported_os': 'SupportedOS',
'reserved_ip': 'ReservedIP',
'reserved_ips': 'ReservedIPs',
'aad_tenant_id': 'AADTenantID',
'start_ip_address': 'StartIPAddress',
'end_ip_address': 'EndIPAddress',
} }
@@ -428,6 +454,25 @@ def _convert_response_to_feeds(response, convert_func):
return feeds return feeds
def _convert_xml_to_windows_azure_object(xmlstr, azure_type, include_id=True, use_title_as_id=True):
xmldoc = minidom.parseString(xmlstr)
return_obj = azure_type()
xml_name = azure_type._xml_name if hasattr(azure_type, '_xml_name') else azure_type.__name__
# Only one entry here
for xml_entry in _get_children_from_path(xmldoc,
'entry'):
for node in _get_children_from_path(xml_entry,
'content',
xml_name):
_fill_data_to_return_object(node, return_obj)
for name, value in _get_entry_properties_from_node(xml_entry,
include_id=include_id,
use_title_as_id=use_title_as_id).items():
setattr(return_obj, name, value)
return return_obj
def _validate_type_bytes(param_name, param): def _validate_type_bytes(param_name, param):
if not isinstance(param, bytes): if not isinstance(param, bytes):
raise TypeError(_ERROR_VALUE_SHOULD_BE_BYTES.format(param_name)) raise TypeError(_ERROR_VALUE_SHOULD_BE_BYTES.format(param_name))
@@ -675,6 +720,13 @@ def _parse_response(response, return_type):
''' '''
return _parse_response_body_from_xml_text(response.body, return_type) return _parse_response_body_from_xml_text(response.body, return_type)
def _parse_service_resources_response(response, return_type):
'''
Parse the HTTPResponse's body and fill all the data into a class of
return_type.
'''
return _parse_response_body_from_service_resources_xml_text(response.body, return_type)
def _fill_data_to_return_object(node, return_obj): def _fill_data_to_return_object(node, return_obj):
members = dict(vars(return_obj)) members = dict(vars(return_obj))
@@ -700,6 +752,12 @@ def _fill_data_to_return_object(node, return_obj):
value.pair_xml_element_name, value.pair_xml_element_name,
value.key_xml_element_name, value.key_xml_element_name,
value.value_xml_element_name)) value.value_xml_element_name))
elif isinstance(value, _xml_attribute):
real_value = None
if node.hasAttribute(value.xml_element_name):
real_value = node.getAttribute(value.xml_element_name)
if real_value is not None:
setattr(return_obj, name, real_value)
elif isinstance(value, WindowsAzureData): elif isinstance(value, WindowsAzureData):
setattr(return_obj, setattr(return_obj,
name, name,
@@ -737,11 +795,24 @@ def _parse_response_body_from_xml_text(respbody, return_type):
''' '''
doc = minidom.parseString(respbody) doc = minidom.parseString(respbody)
return_obj = return_type() return_obj = return_type()
for node in _get_child_nodes(doc, return_type.__name__): xml_name = return_type._xml_name if hasattr(return_type, '_xml_name') else return_type.__name__
for node in _get_child_nodes(doc, xml_name):
_fill_data_to_return_object(node, return_obj) _fill_data_to_return_object(node, return_obj)
return return_obj return return_obj
def _parse_response_body_from_service_resources_xml_text(respbody, return_type):
'''
parse the xml and fill all the data into a class of return_type
'''
doc = minidom.parseString(respbody)
return_obj = _list_of(return_type)
for node in _get_children_from_path(doc, "ServiceResources", "ServiceResource"):
local_obj = return_type()
_fill_data_to_return_object(node, local_obj)
return_obj.append(local_obj)
return return_obj
class _dict_of(dict): class _dict_of(dict):
@@ -781,6 +852,15 @@ class _scalar_list_of(list):
self.xml_element_name = xml_element_name self.xml_element_name = xml_element_name
super(_scalar_list_of, self).__init__() super(_scalar_list_of, self).__init__()
class _xml_attribute:
"""a accessor to XML attributes
expected to go in it along with its xml element name.
Used for deserialization and construction"""
def __init__(self, xml_element_name):
self.xml_element_name = xml_element_name
def _update_request_uri_query_local_storage(request, use_local_storage): def _update_request_uri_query_local_storage(request, use_local_storage):
''' create correct uri and query for the request ''' ''' create correct uri and query for the request '''
@@ -903,3 +983,17 @@ def _parse_response_for_dict_filter(response, filter):
return return_dict return return_dict
else: else:
return None return None
def _sign_string(key, string_to_sign, key_is_base64=True):
if key_is_base64:
key = _decode_base64_to_bytes(key)
else:
if isinstance(key, _unicode_type):
key = key.encode('utf-8')
if isinstance(string_to_sign, _unicode_type):
string_to_sign = string_to_sign.encode('utf-8')
signed_hmac_sha256 = hmac.HMAC(key, string_to_sign, hashlib.sha256)
digest = signed_hmac_sha256.digest()
encoded_digest = _encode_base64(digest)
return encoded_digest

View File

@@ -0,0 +1,81 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{25b2c65a-0553-4452-8907-8b5b17544e68}</ProjectGuid>
<ProjectHome>
</ProjectHome>
<StartupFile>storage\blobservice.py</StartupFile>
<SearchPath>..</SearchPath>
<WorkingDirectory>.</WorkingDirectory>
<OutputPath>.</OutputPath>
<Name>azure</Name>
<RootNamespace>azure</RootNamespace>
<IsWindowsApplication>False</IsWindowsApplication>
<LaunchProvider>Standard Python launcher</LaunchProvider>
<CommandLineArguments />
<InterpreterPath />
<InterpreterArguments />
<InterpreterId>{2af0f10d-7135-4994-9156-5d01c9c11b7e}</InterpreterId>
<InterpreterVersion>2.7</InterpreterVersion>
<SccProjectName>SAK</SccProjectName>
<SccProvider>SAK</SccProvider>
<SccAuxPath>SAK</SccAuxPath>
<SccLocalPath>SAK</SccLocalPath>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<DebugSymbols>true</DebugSymbols>
<EnableUnmanagedDebugging>false</EnableUnmanagedDebugging>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<DebugSymbols>true</DebugSymbols>
<EnableUnmanagedDebugging>false</EnableUnmanagedDebugging>
</PropertyGroup>
<ItemGroup>
<Compile Include="http\batchclient.py" />
<Compile Include="http\httpclient.py" />
<Compile Include="http\requestsclient.py" />
<Compile Include="http\winhttp.py" />
<Compile Include="http\__init__.py" />
<Compile Include="servicemanagement\schedulermanagementservice.py" />
<Compile Include="servicemanagement\servicebusmanagementservice.py" />
<Compile Include="servicemanagement\servicemanagementclient.py" />
<Compile Include="servicemanagement\servicemanagementservice.py" />
<Compile Include="servicemanagement\sqldatabasemanagementservice.py" />
<Compile Include="servicemanagement\websitemanagementservice.py" />
<Compile Include="servicemanagement\__init__.py" />
<Compile Include="servicebus\servicebusservice.py" />
<Compile Include="storage\blobservice.py" />
<Compile Include="storage\queueservice.py" />
<Compile Include="storage\cloudstorageaccount.py" />
<Compile Include="storage\tableservice.py" />
<Compile Include="storage\sharedaccesssignature.py" />
<Compile Include="__init__.py" />
<Compile Include="servicebus\__init__.py" />
<Compile Include="storage\storageclient.py" />
<Compile Include="storage\__init__.py" />
</ItemGroup>
<ItemGroup>
<Folder Include="http" />
<Folder Include="servicemanagement" />
<Folder Include="servicebus" />
<Folder Include="storage" />
</ItemGroup>
<ItemGroup>
<InterpreterReference Include="{2af0f10d-7135-4994-9156-5d01c9c11b7e}\2.6" />
<InterpreterReference Include="{2af0f10d-7135-4994-9156-5d01c9c11b7e}\2.7" />
<InterpreterReference Include="{2af0f10d-7135-4994-9156-5d01c9c11b7e}\3.3" />
<InterpreterReference Include="{2af0f10d-7135-4994-9156-5d01c9c11b7e}\3.4" />
<InterpreterReference Include="{9a7a9026-48c1-4688-9d5d-e5699d47d074}\2.7" />
<InterpreterReference Include="{9a7a9026-48c1-4688-9d5d-e5699d47d074}\3.3" />
<InterpreterReference Include="{9a7a9026-48c1-4688-9d5d-e5699d47d074}\3.4" />
</ItemGroup>
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
<PtvsTargetsFile>$(VSToolsPath)\Python Tools\Microsoft.PythonTools.targets</PtvsTargetsFile>
</PropertyGroup>
<Import Condition="Exists($(PtvsTargetsFile))" Project="$(PtvsTargetsFile)" />
<Import Condition="!Exists($(PtvsTargetsFile))" Project="$(MSBuildToolsPath)\Microsoft.Common.targets" />
</Project>

View File

@@ -36,6 +36,8 @@ else:
from azure.http import HTTPError, HTTPResponse from azure.http import HTTPError, HTTPResponse
from azure import _USER_AGENT_STRING, _update_request_uri_query from azure import _USER_AGENT_STRING, _update_request_uri_query
DEBUG_REQUESTS = False
DEBUG_RESPONSES = False
class _HTTPClient(object): class _HTTPClient(object):
@@ -44,8 +46,7 @@ class _HTTPClient(object):
''' '''
def __init__(self, service_instance, cert_file=None, account_name=None, def __init__(self, service_instance, cert_file=None, account_name=None,
account_key=None, service_namespace=None, issuer=None, account_key=None, protocol='https', request_session=None):
protocol='https'):
''' '''
service_instance: service client instance. service_instance: service client instance.
cert_file: cert_file:
@@ -53,10 +54,9 @@ class _HTTPClient(object):
service management. service management.
account_name: the storage account. account_name: the storage account.
account_key: account_key:
the storage account access key for storage services or servicebus the storage account access key.
access key for service bus service. request_session:
service_namespace: the service namespace for service bus. session object created with requests library (or compatible).
issuer: the issuer for service bus service.
''' '''
self.service_instance = service_instance self.service_instance = service_instance
self.status = None self.status = None
@@ -65,14 +65,16 @@ class _HTTPClient(object):
self.cert_file = cert_file self.cert_file = cert_file
self.account_name = account_name self.account_name = account_name
self.account_key = account_key self.account_key = account_key
self.service_namespace = service_namespace
self.issuer = issuer
self.protocol = protocol self.protocol = protocol
self.proxy_host = None self.proxy_host = None
self.proxy_port = None self.proxy_port = None
self.proxy_user = None self.proxy_user = None
self.proxy_password = None self.proxy_password = None
self.use_httplib = self.should_use_httplib() self.request_session = request_session
if request_session:
self.use_httplib = True
else:
self.use_httplib = self.should_use_httplib()
def should_use_httplib(self): def should_use_httplib(self):
if sys.platform.lower().startswith('win') and self.cert_file: if sys.platform.lower().startswith('win') and self.cert_file:
@@ -111,6 +113,13 @@ class _HTTPClient(object):
self.proxy_user = user self.proxy_user = user
self.proxy_password = password self.proxy_password = password
def get_uri(self, request):
''' Return the target uri for the request.'''
protocol = request.protocol_override \
if request.protocol_override else self.protocol
port = HTTP_PORT if protocol == 'http' else HTTPS_PORT
return protocol + '://' + request.host + ':' + str(port) + request.path
def get_connection(self, request): def get_connection(self, request):
''' Create connection for the request. ''' ''' Create connection for the request. '''
protocol = request.protocol_override \ protocol = request.protocol_override \
@@ -118,7 +127,12 @@ class _HTTPClient(object):
target_host = request.host target_host = request.host
target_port = HTTP_PORT if protocol == 'http' else HTTPS_PORT target_port = HTTP_PORT if protocol == 'http' else HTTPS_PORT
if not self.use_httplib: if self.request_session:
import azure.http.requestsclient
connection = azure.http.requestsclient._RequestsConnection(
target_host, protocol, self.request_session)
#TODO: proxy stuff
elif not self.use_httplib:
import azure.http.winhttp import azure.http.winhttp
connection = azure.http.winhttp._HTTPConnection( connection = azure.http.winhttp._HTTPConnection(
target_host, cert_file=self.cert_file, protocol=protocol) target_host, cert_file=self.cert_file, protocol=protocol)
@@ -191,6 +205,13 @@ class _HTTPClient(object):
self.send_request_headers(connection, request.headers) self.send_request_headers(connection, request.headers)
self.send_request_body(connection, request.body) self.send_request_body(connection, request.body)
if DEBUG_REQUESTS and request.body:
print('request:')
try:
print(request.body)
except:
pass
resp = connection.getresponse() resp = connection.getresponse()
self.status = int(resp.status) self.status = int(resp.status)
self.message = resp.reason self.message = resp.reason
@@ -206,6 +227,13 @@ class _HTTPClient(object):
elif resp.length > 0: elif resp.length > 0:
respbody = resp.read(resp.length) respbody = resp.read(resp.length)
if DEBUG_RESPONSES and respbody:
print('response:')
try:
print(respbody)
except:
pass
response = HTTPResponse( response = HTTPResponse(
int(resp.status), resp.reason, headers, respbody) int(resp.status), resp.reason, headers, respbody)
if self.status == 307: if self.status == 307:

View File

@@ -0,0 +1,74 @@
#-------------------------------------------------------------------------
# Copyright (c) Microsoft. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#--------------------------------------------------------------------------
class _Response(object):
''' Response class corresponding to the response returned from httplib
HTTPConnection. '''
def __init__(self, response):
self.status = response.status_code
self.reason = response.reason
self.respbody = response.content
self.length = len(response.content)
self.headers = []
for key, name in response.headers.items():
self.headers.append((key.lower(), name))
def getheaders(self):
'''Returns response headers.'''
return self.headers
def read(self, _length):
'''Returns response body. '''
return self.respbody[:_length]
class _RequestsConnection(object):
def __init__(self, host, protocol, session):
self.host = host
self.protocol = protocol
self.session = session
self.headers = {}
self.method = None
self.body = None
self.response = None
self.uri = None
def close(self):
pass
def set_tunnel(self, host, port=None, headers=None):
pass
def set_proxy_credentials(self, user, password):
pass
def putrequest(self, method, uri):
self.method = method
self.uri = self.protocol + '://' + self.host + uri
def putheader(self, name, value):
self.headers[name] = value
def endheaders(self):
pass
def send(self, request_body):
self.response = self.session.request(self.method, self.uri, data=request_body, headers=self.headers)
def getresponse(self):
return _Response(self.response)

View File

@@ -13,6 +13,7 @@
# limitations under the License. # limitations under the License.
#-------------------------------------------------------------------------- #--------------------------------------------------------------------------
import ast import ast
import json
import sys import sys
from datetime import datetime from datetime import datetime
@@ -167,16 +168,16 @@ class Message(WindowsAzureData):
# extracts the topic and subscriptions name if it is topic message. # extracts the topic and subscriptions name if it is topic message.
if location: if location:
if '/subscriptions/' in location: if '/subscriptions/' in location:
pos = location.find('/subscriptions/') pos = location.find(service_bus_service.host_base.lower())+1
pos1 = location.rfind('/', 0, pos - 1) pos1 = location.find('/subscriptions/')
self._topic_name = location[pos1 + 1:pos] self._topic_name = location[pos+len(service_bus_service.host_base):pos1]
pos += len('/subscriptions/') pos = pos1 + len('/subscriptions/')
pos1 = location.find('/', pos) pos1 = location.find('/', pos)
self._subscription_name = location[pos:pos1] self._subscription_name = location[pos:pos1]
elif '/messages/' in location: elif '/messages/' in location:
pos = location.find('/messages/') pos = location.find(service_bus_service.host_base.lower())+1
pos1 = location.rfind('/', 0, pos - 1) pos1 = location.find('/messages/')
self._queue_name = location[pos1 + 1:pos] self._queue_name = location[pos+len(service_bus_service.host_base):pos1]
def delete(self): def delete(self):
''' Deletes itself if find queue name or topic name and subscription ''' Deletes itself if find queue name or topic name and subscription
@@ -255,7 +256,7 @@ def _create_message(response, service_instance):
# gets all information from respheaders. # gets all information from respheaders.
for name, value in response.headers: for name, value in response.headers:
if name.lower() == 'brokerproperties': if name.lower() == 'brokerproperties':
broker_properties = ast.literal_eval(value) broker_properties = json.loads(value)
elif name.lower() == 'content-type': elif name.lower() == 'content-type':
message_type = value message_type = value
elif name.lower() == 'location': elif name.lower() == 'location':

View File

@@ -12,6 +12,7 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
#-------------------------------------------------------------------------- #--------------------------------------------------------------------------
import datetime
import os import os
import time import time
@@ -21,10 +22,13 @@ from azure import (
_convert_response_to_feeds, _convert_response_to_feeds,
_dont_fail_not_exist, _dont_fail_not_exist,
_dont_fail_on_exist, _dont_fail_on_exist,
_encode_base64,
_get_request_body, _get_request_body,
_get_request_body_bytes_only, _get_request_body_bytes_only,
_int_or_none, _int_or_none,
_sign_string,
_str, _str,
_unicode_type,
_update_request_uri_query, _update_request_uri_query,
url_quote, url_quote,
url_unquote, url_unquote,
@@ -55,44 +59,94 @@ from azure.servicebus import (
_service_bus_error_handler, _service_bus_error_handler,
) )
# Token cache for Authentication
# Shared by the different instances of ServiceBusService
_tokens = {}
class ServiceBusService(object): class ServiceBusService(object):
def __init__(self, service_namespace=None, account_key=None, issuer=None, def __init__(self, service_namespace=None, account_key=None, issuer=None,
x_ms_version='2011-06-01', host_base=SERVICE_BUS_HOST_BASE): x_ms_version='2011-06-01', host_base=SERVICE_BUS_HOST_BASE,
# x_ms_version is not used, but the parameter is kept for backwards shared_access_key_name=None, shared_access_key_value=None,
# compatibility authentication=None):
'''
Initializes the service bus service for a namespace with the specified
authentication settings (SAS or ACS).
service_namespace:
Service bus namespace, required for all operations. If None,
the value is set to the AZURE_SERVICEBUS_NAMESPACE env variable.
account_key:
ACS authentication account key. If None, the value is set to the
AZURE_SERVICEBUS_ACCESS_KEY env variable.
Note that if both SAS and ACS settings are specified, SAS is used.
issuer:
ACS authentication issuer. If None, the value is set to the
AZURE_SERVICEBUS_ISSUER env variable.
Note that if both SAS and ACS settings are specified, SAS is used.
x_ms_version: Unused. Kept for backwards compatibility.
host_base:
Optional. Live host base url. Defaults to Azure url. Override this
for on-premise.
shared_access_key_name:
SAS authentication key name.
Note that if both SAS and ACS settings are specified, SAS is used.
shared_access_key_value:
SAS authentication key value.
Note that if both SAS and ACS settings are specified, SAS is used.
authentication:
Instance of authentication class. If this is specified, then
ACS and SAS parameters are ignored.
'''
self.requestid = None self.requestid = None
self.service_namespace = service_namespace self.service_namespace = service_namespace
self.account_key = account_key
self.issuer = issuer
self.host_base = host_base self.host_base = host_base
# Get service namespace, account key and issuer.
# If they are set when constructing, then use them, else find them
# from environment variables.
if not self.service_namespace: if not self.service_namespace:
self.service_namespace = os.environ.get(AZURE_SERVICEBUS_NAMESPACE) self.service_namespace = os.environ.get(AZURE_SERVICEBUS_NAMESPACE)
if not self.account_key:
self.account_key = os.environ.get(AZURE_SERVICEBUS_ACCESS_KEY)
if not self.issuer:
self.issuer = os.environ.get(AZURE_SERVICEBUS_ISSUER)
if not self.service_namespace or \ if not self.service_namespace:
not self.account_key or not self.issuer: raise WindowsAzureError('You need to provide servicebus namespace')
raise WindowsAzureError(
'You need to provide servicebus namespace, access key and Issuer')
self._httpclient = _HTTPClient(service_instance=self, if authentication:
service_namespace=self.service_namespace, self.authentication = authentication
account_key=self.account_key, else:
issuer=self.issuer) if not account_key:
account_key = os.environ.get(AZURE_SERVICEBUS_ACCESS_KEY)
if not issuer:
issuer = os.environ.get(AZURE_SERVICEBUS_ISSUER)
if shared_access_key_name and shared_access_key_value:
self.authentication = ServiceBusSASAuthentication(
shared_access_key_name,
shared_access_key_value)
elif account_key and issuer:
self.authentication = ServiceBusWrapTokenAuthentication(
account_key,
issuer)
else:
raise WindowsAzureError(
'You need to provide servicebus access key and Issuer OR shared access key and value')
self._httpclient = _HTTPClient(service_instance=self)
self._filter = self._httpclient.perform_request self._filter = self._httpclient.perform_request
# Backwards compatibility:
# account_key and issuer used to be stored on the service class, they are
# now stored on the authentication class.
@property
def account_key(self):
return self.authentication.account_key
@account_key.setter
def account_key(self, value):
self.authentication.account_key = value
@property
def issuer(self):
return self.authentication.issuer
@issuer.setter
def issuer(self, value):
self.authentication.issuer = value
def with_filter(self, filter): def with_filter(self, filter):
''' '''
Returns a new service which will process requests with the specified Returns a new service which will process requests with the specified
@@ -102,8 +156,10 @@ class ServiceBusService(object):
request, pass it off to the next lambda, and then perform any request, pass it off to the next lambda, and then perform any
post-processing on the response. post-processing on the response.
''' '''
res = ServiceBusService(self.service_namespace, self.account_key, res = ServiceBusService(
self.issuer) service_namespace=self.service_namespace,
authentication=self.authentication)
old_filter = self._filter old_filter = self._filter
def new_filter(request): def new_filter(request):
@@ -855,17 +911,30 @@ class ServiceBusService(object):
('Content-Type', ('Content-Type',
'application/atom+xml;type=entry;charset=utf-8')) 'application/atom+xml;type=entry;charset=utf-8'))
# Adds authoriaztion header for authentication. # Adds authorization header for authentication.
request.headers.append( self.authentication.sign_request(request, self._httpclient)
('Authorization', self._sign_service_bus_request(request)))
return request.headers return request.headers
def _sign_service_bus_request(self, request):
''' return the signed string with token. '''
# Token cache for Authentication
# Shared by the different instances of ServiceBusWrapTokenAuthentication
_tokens = {}
class ServiceBusWrapTokenAuthentication:
def __init__(self, account_key, issuer):
self.account_key = account_key
self.issuer = issuer
def sign_request(self, request, httpclient):
request.headers.append(
('Authorization', self._get_authorization(request, httpclient)))
def _get_authorization(self, request, httpclient):
''' return the signed string with token. '''
return 'WRAP access_token="' + \ return 'WRAP access_token="' + \
self._get_token(request.host, request.path) + '"' self._get_token(request.host, request.path, httpclient) + '"'
def _token_is_expired(self, token): def _token_is_expired(self, token):
''' Check if token expires or not. ''' ''' Check if token expires or not. '''
@@ -878,7 +947,7 @@ class ServiceBusService(object):
# token to server. # token to server.
return (token_expire_time - time_now) < 30 return (token_expire_time - time_now) < 30
def _get_token(self, host, path): def _get_token(self, host, path, httpclient):
''' '''
Returns token for the request. Returns token for the request.
@@ -905,10 +974,38 @@ class ServiceBusService(object):
'&wrap_scope=' + '&wrap_scope=' +
url_quote('http://' + host + path)).encode('utf-8') url_quote('http://' + host + path)).encode('utf-8')
request.headers.append(('Content-Length', str(len(request.body)))) request.headers.append(('Content-Length', str(len(request.body))))
resp = self._httpclient.perform_request(request) resp = httpclient.perform_request(request)
token = resp.body.decode('utf-8') token = resp.body.decode('utf-8')
token = url_unquote(token[token.find('=') + 1:token.rfind('&')]) token = url_unquote(token[token.find('=') + 1:token.rfind('&')])
_tokens[wrap_scope] = token _tokens[wrap_scope] = token
return token return token
class ServiceBusSASAuthentication:
def __init__(self, key_name, key_value):
self.key_name = key_name
self.key_value = key_value
def sign_request(self, request, httpclient):
request.headers.append(
('Authorization', self._get_authorization(request, httpclient)))
def _get_authorization(self, request, httpclient):
uri = httpclient.get_uri(request)
uri = url_quote(uri, '').lower()
expiry = str(self._get_expiry())
to_sign = uri + '\n' + expiry
signature = url_quote(_sign_string(self.key_value, to_sign, False), '')
auth_format = 'SharedAccessSignature sig={0}&se={1}&skn={2}&sr={3}'
auth = auth_format.format(signature, expiry, self.key_name, uri)
return auth
def _get_expiry(self):
'''Returns the UTC datetime, in seconds since Epoch, when this signed
request expires (5 minutes from now).'''
return int(round(time.time() + 300))

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,70 @@
#-------------------------------------------------------------------------
# Copyright (c) Microsoft. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#--------------------------------------------------------------------------
from azure import (
MANAGEMENT_HOST,
_str
)
from azure.servicemanagement import (
CloudServices,
)
from azure.servicemanagement.servicemanagementclient import (
_ServiceManagementClient,
)
class SchedulerManagementService(_ServiceManagementClient):
''' Note that this class is a preliminary work on Scheduler
management. Since it lack a lot a features, final version
can be slightly different from the current one.
'''
def __init__(self, subscription_id=None, cert_file=None,
host=MANAGEMENT_HOST, request_session=None):
'''
Initializes the scheduler management service.
subscription_id: Subscription to manage.
cert_file:
Path to .pem certificate file (httplib), or location of the
certificate in your Personal certificate store (winhttp) in the
CURRENT_USER\my\CertificateName format.
If a request_session is specified, then this is unused.
host: Live ServiceClient URL. Defaults to Azure public cloud.
request_session:
Session object to use for http requests. If this is specified, it
replaces the default use of httplib or winhttp. Also, the cert_file
parameter is unused when a session is passed in.
The session object handles authentication, and as such can support
multiple types of authentication: .pem certificate, oauth.
For example, you can pass in a Session instance from the requests
library. To use .pem certificate authentication with requests
library, set the path to the .pem file on the session.cert
attribute.
'''
super(SchedulerManagementService, self).__init__(
subscription_id, cert_file, host, request_session)
#--Operations for scheduler ----------------------------------------
def list_cloud_services(self):
'''
List the cloud services for scheduling defined on the account.
'''
return self._perform_get(self._get_list_cloud_services_path(),
CloudServices)
#--Helper functions --------------------------------------------------
def _get_list_cloud_services_path(self):
return self._get_path('cloudservices', None)

View File

@@ -17,23 +17,56 @@ from azure import (
_convert_response_to_feeds, _convert_response_to_feeds,
_str, _str,
_validate_not_none, _validate_not_none,
) _convert_xml_to_windows_azure_object,
)
from azure.servicemanagement import ( from azure.servicemanagement import (
_ServiceBusManagementXmlSerializer, _ServiceBusManagementXmlSerializer,
) QueueDescription,
TopicDescription,
NotificationHubDescription,
RelayDescription,
MetricProperties,
MetricValues,
MetricRollups,
)
from azure.servicemanagement.servicemanagementclient import ( from azure.servicemanagement.servicemanagementclient import (
_ServiceManagementClient, _ServiceManagementClient,
) )
from functools import partial
X_MS_VERSION = '2012-03-01'
class ServiceBusManagementService(_ServiceManagementClient): class ServiceBusManagementService(_ServiceManagementClient):
def __init__(self, subscription_id=None, cert_file=None, def __init__(self, subscription_id=None, cert_file=None,
host=MANAGEMENT_HOST): host=MANAGEMENT_HOST, request_session=None):
super(ServiceBusManagementService, self).__init__( '''
subscription_id, cert_file, host) Initializes the service bus management service.
#--Operations for service bus ---------------------------------------- subscription_id: Subscription to manage.
cert_file:
Path to .pem certificate file (httplib), or location of the
certificate in your Personal certificate store (winhttp) in the
CURRENT_USER\my\CertificateName format.
If a request_session is specified, then this is unused.
host: Live ServiceClient URL. Defaults to Azure public cloud.
request_session:
Session object to use for http requests. If this is specified, it
replaces the default use of httplib or winhttp. Also, the cert_file
parameter is unused when a session is passed in.
The session object handles authentication, and as such can support
multiple types of authentication: .pem certificate, oauth.
For example, you can pass in a Session instance from the requests
library. To use .pem certificate authentication with requests
library, set the path to the .pem file on the session.cert
attribute.
'''
super(ServiceBusManagementService, self).__init__(
subscription_id, cert_file, host, request_session)
self.x_ms_version = X_MS_VERSION
# Operations for service bus ----------------------------------------
def get_regions(self): def get_regions(self):
''' '''
Get list of available service bus regions. Get list of available service bus regions.
@@ -111,3 +144,391 @@ class ServiceBusManagementService(_ServiceManagementClient):
return _ServiceBusManagementXmlSerializer.xml_to_namespace_availability( return _ServiceBusManagementXmlSerializer.xml_to_namespace_availability(
response.body) response.body)
def list_queues(self, name):
'''
Enumerates the queues in the service namespace.
name: Name of the service bus namespace.
'''
_validate_not_none('name', name)
response = self._perform_get(
self._get_list_queues_path(name),
None)
return _convert_response_to_feeds(response,
partial(_convert_xml_to_windows_azure_object,
azure_type=QueueDescription))
def list_topics(self, name):
'''
Retrieves the topics in the service namespace.
name: Name of the service bus namespace.
'''
response = self._perform_get(
self._get_list_topics_path(name),
None)
return _convert_response_to_feeds(response,
partial(_convert_xml_to_windows_azure_object,
azure_type=TopicDescription))
def list_notification_hubs(self, name):
'''
Retrieves the notification hubs in the service namespace.
name: Name of the service bus namespace.
'''
response = self._perform_get(
self._get_list_notification_hubs_path(name),
None)
return _convert_response_to_feeds(response,
partial(_convert_xml_to_windows_azure_object,
azure_type=NotificationHubDescription))
def list_relays(self, name):
'''
Retrieves the relays in the service namespace.
name: Name of the service bus namespace.
'''
response = self._perform_get(
self._get_list_relays_path(name),
None)
return _convert_response_to_feeds(response,
partial(_convert_xml_to_windows_azure_object,
azure_type=RelayDescription))
def get_supported_metrics_queue(self, name, queue_name):
'''
Retrieves the list of supported metrics for this namespace and queue
name: Name of the service bus namespace.
queue_name: Name of the service bus queue in this namespace.
'''
response = self._perform_get(
self._get_get_supported_metrics_queue_path(name, queue_name),
None)
return _convert_response_to_feeds(response,
partial(_ServiceBusManagementXmlSerializer.xml_to_metrics,
object_type=MetricProperties))
def get_supported_metrics_topic(self, name, topic_name):
'''
Retrieves the list of supported metrics for this namespace and topic
name: Name of the service bus namespace.
topic_name: Name of the service bus queue in this namespace.
'''
response = self._perform_get(
self._get_get_supported_metrics_topic_path(name, topic_name),
None)
return _convert_response_to_feeds(response,
partial(_ServiceBusManagementXmlSerializer.xml_to_metrics,
object_type=MetricProperties))
def get_supported_metrics_notification_hub(self, name, hub_name):
'''
Retrieves the list of supported metrics for this namespace and topic
name: Name of the service bus namespace.
hub_name: Name of the service bus notification hub in this namespace.
'''
response = self._perform_get(
self._get_get_supported_metrics_hub_path(name, hub_name),
None)
return _convert_response_to_feeds(response,
partial(_ServiceBusManagementXmlSerializer.xml_to_metrics,
object_type=MetricProperties))
def get_supported_metrics_relay(self, name, relay_name):
'''
Retrieves the list of supported metrics for this namespace and relay
name: Name of the service bus namespace.
relay_name: Name of the service bus relay in this namespace.
'''
response = self._perform_get(
self._get_get_supported_metrics_relay_path(name, relay_name),
None)
return _convert_response_to_feeds(response,
partial(_ServiceBusManagementXmlSerializer.xml_to_metrics,
object_type=MetricProperties))
def get_metrics_data_queue(self, name, queue_name, metric, rollup, filter_expresssion):
'''
Retrieves the list of supported metrics for this namespace and queue
name: Name of the service bus namespace.
queue_name: Name of the service bus queue in this namespace.
metric: name of a supported metric
rollup: name of a supported rollup
filter_expression: filter, for instance "$filter=Timestamp gt datetime'2014-10-01T00:00:00Z'"
'''
response = self._perform_get(
self._get_get_metrics_data_queue_path(name, queue_name, metric, rollup, filter_expresssion),
None)
return _convert_response_to_feeds(response,
partial(_ServiceBusManagementXmlSerializer.xml_to_metrics,
object_type=MetricValues))
def get_metrics_data_topic(self, name, topic_name, metric, rollup, filter_expresssion):
'''
Retrieves the list of supported metrics for this namespace and topic
name: Name of the service bus namespace.
topic_name: Name of the service bus queue in this namespace.
metric: name of a supported metric
rollup: name of a supported rollup
filter_expression: filter, for instance "$filter=Timestamp gt datetime'2014-10-01T00:00:00Z'"
'''
response = self._perform_get(
self._get_get_metrics_data_topic_path(name, topic_name, metric, rollup, filter_expresssion),
None)
return _convert_response_to_feeds(response,
partial(_ServiceBusManagementXmlSerializer.xml_to_metrics,
object_type=MetricValues))
def get_metrics_data_notification_hub(self, name, hub_name, metric, rollup, filter_expresssion):
'''
Retrieves the list of supported metrics for this namespace and topic
name: Name of the service bus namespace.
hub_name: Name of the service bus notification hub in this namespace.
metric: name of a supported metric
rollup: name of a supported rollup
filter_expression: filter, for instance "$filter=Timestamp gt datetime'2014-10-01T00:00:00Z'"
'''
response = self._perform_get(
self._get_get_metrics_data_hub_path(name, hub_name, metric, rollup, filter_expresssion),
None)
return _convert_response_to_feeds(response,
partial(_ServiceBusManagementXmlSerializer.xml_to_metrics,
object_type=MetricValues))
def get_metrics_data_relay(self, name, relay_name, metric, rollup, filter_expresssion):
'''
Retrieves the list of supported metrics for this namespace and relay
name: Name of the service bus namespace.
relay_name: Name of the service bus relay in this namespace.
metric: name of a supported metric
rollup: name of a supported rollup
filter_expression: filter, for instance "$filter=Timestamp gt datetime'2014-10-01T00:00:00Z'"
'''
response = self._perform_get(
self._get_get_metrics_data_relay_path(name, relay_name, metric, rollup, filter_expresssion),
None)
return _convert_response_to_feeds(response,
partial(_ServiceBusManagementXmlSerializer.xml_to_metrics,
object_type=MetricValues))
def get_metrics_rollups_queue(self, name, queue_name, metric):
'''
This operation gets rollup data for Service Bus metrics queue.
Rollup data includes the time granularity for the telemetry aggregation as well as
the retention settings for each time granularity.
name: Name of the service bus namespace.
queue_name: Name of the service bus queue in this namespace.
metric: name of a supported metric
'''
response = self._perform_get(
self._get_get_metrics_rollup_queue_path(name, queue_name, metric),
None)
return _convert_response_to_feeds(response,
partial(_ServiceBusManagementXmlSerializer.xml_to_metrics,
object_type=MetricRollups))
def get_metrics_rollups_topic(self, name, topic_name, metric):
'''
This operation gets rollup data for Service Bus metrics topic.
Rollup data includes the time granularity for the telemetry aggregation as well as
the retention settings for each time granularity.
name: Name of the service bus namespace.
topic_name: Name of the service bus queue in this namespace.
metric: name of a supported metric
'''
response = self._perform_get(
self._get_get_metrics_rollup_topic_path(name, topic_name, metric),
None)
return _convert_response_to_feeds(response,
partial(_ServiceBusManagementXmlSerializer.xml_to_metrics,
object_type=MetricRollups))
def get_metrics_rollups_notification_hub(self, name, hub_name, metric):
'''
This operation gets rollup data for Service Bus metrics notification hub.
Rollup data includes the time granularity for the telemetry aggregation as well as
the retention settings for each time granularity.
name: Name of the service bus namespace.
hub_name: Name of the service bus notification hub in this namespace.
metric: name of a supported metric
'''
response = self._perform_get(
self._get_get_metrics_rollup_hub_path(name, hub_name, metric),
None)
return _convert_response_to_feeds(response,
partial(_ServiceBusManagementXmlSerializer.xml_to_metrics,
object_type=MetricRollups))
def get_metrics_rollups_relay(self, name, relay_name, metric):
'''
This operation gets rollup data for Service Bus metrics relay.
Rollup data includes the time granularity for the telemetry aggregation as well as
the retention settings for each time granularity.
name: Name of the service bus namespace.
relay_name: Name of the service bus relay in this namespace.
metric: name of a supported metric
'''
response = self._perform_get(
self._get_get_metrics_rollup_relay_path(name, relay_name, metric),
None)
return _convert_response_to_feeds(response,
partial(_ServiceBusManagementXmlSerializer.xml_to_metrics,
object_type=MetricRollups))
# Helper functions --------------------------------------------------
def _get_list_queues_path(self, namespace_name):
return self._get_path('services/serviceBus/Namespaces/',
namespace_name) + '/Queues'
def _get_list_topics_path(self, namespace_name):
return self._get_path('services/serviceBus/Namespaces/',
namespace_name) + '/Topics'
def _get_list_notification_hubs_path(self, namespace_name):
return self._get_path('services/serviceBus/Namespaces/',
namespace_name) + '/NotificationHubs'
def _get_list_relays_path(self, namespace_name):
return self._get_path('services/serviceBus/Namespaces/',
namespace_name) + '/Relays'
def _get_get_supported_metrics_queue_path(self, namespace_name, queue_name):
return self._get_path('services/serviceBus/Namespaces/',
namespace_name) + '/Queues/' + _str(queue_name) + '/Metrics'
def _get_get_supported_metrics_topic_path(self, namespace_name, topic_name):
return self._get_path('services/serviceBus/Namespaces/',
namespace_name) + '/Topics/' + _str(topic_name) + '/Metrics'
def _get_get_supported_metrics_hub_path(self, namespace_name, hub_name):
return self._get_path('services/serviceBus/Namespaces/',
namespace_name) + '/NotificationHubs/' + _str(hub_name) + '/Metrics'
def _get_get_supported_metrics_relay_path(self, namespace_name, queue_name):
return self._get_path('services/serviceBus/Namespaces/',
namespace_name) + '/Relays/' + _str(queue_name) + '/Metrics'
def _get_get_metrics_data_queue_path(self, namespace_name, queue_name, metric, rollup, filter_expr):
return "".join([
self._get_path('services/serviceBus/Namespaces/', namespace_name),
'/Queues/',
_str(queue_name),
'/Metrics/',
_str(metric),
'/Rollups/',
_str(rollup),
'/Values?',
filter_expr
])
def _get_get_metrics_data_topic_path(self, namespace_name, queue_name, metric, rollup, filter_expr):
return "".join([
self._get_path('services/serviceBus/Namespaces/', namespace_name),
'/Topics/',
_str(queue_name),
'/Metrics/',
_str(metric),
'/Rollups/',
_str(rollup),
'/Values?',
filter_expr
])
def _get_get_metrics_data_hub_path(self, namespace_name, queue_name, metric, rollup, filter_expr):
return "".join([
self._get_path('services/serviceBus/Namespaces/', namespace_name),
'/NotificationHubs/',
_str(queue_name),
'/Metrics/',
_str(metric),
'/Rollups/',
_str(rollup),
'/Values?',
filter_expr
])
def _get_get_metrics_data_relay_path(self, namespace_name, queue_name, metric, rollup, filter_expr):
return "".join([
self._get_path('services/serviceBus/Namespaces/', namespace_name),
'/Relays/',
_str(queue_name),
'/Metrics/',
_str(metric),
'/Rollups/',
_str(rollup),
'/Values?',
filter_expr
])
def _get_get_metrics_rollup_queue_path(self, namespace_name, queue_name, metric):
return "".join([
self._get_path('services/serviceBus/Namespaces/', namespace_name),
'/Queues/',
_str(queue_name),
'/Metrics/',
_str(metric),
'/Rollups',
])
def _get_get_metrics_rollup_topic_path(self, namespace_name, queue_name, metric):
return "".join([
self._get_path('services/serviceBus/Namespaces/', namespace_name),
'/Topics/',
_str(queue_name),
'/Metrics/',
_str(metric),
'/Rollups',
])
def _get_get_metrics_rollup_hub_path(self, namespace_name, queue_name, metric):
return "".join([
self._get_path('services/serviceBus/Namespaces/', namespace_name),
'/NotificationHubs/',
_str(queue_name),
'/Metrics/',
_str(metric),
'/Rollups',
])
def _get_get_metrics_rollup_relay_path(self, namespace_name, queue_name, metric):
return "".join([
self._get_path('services/serviceBus/Namespaces/', namespace_name),
'/Relays/',
_str(queue_name),
'/Metrics/',
_str(metric),
'/Rollups',
])

View File

@@ -31,21 +31,24 @@ from azure.servicemanagement import (
AZURE_MANAGEMENT_CERTFILE, AZURE_MANAGEMENT_CERTFILE,
AZURE_MANAGEMENT_SUBSCRIPTIONID, AZURE_MANAGEMENT_SUBSCRIPTIONID,
_management_error_handler, _management_error_handler,
_parse_response_for_async_op, parse_response_for_async_op,
_update_management_header, X_MS_VERSION,
) )
class _ServiceManagementClient(object): class _ServiceManagementClient(object):
def __init__(self, subscription_id=None, cert_file=None, def __init__(self, subscription_id=None, cert_file=None,
host=MANAGEMENT_HOST): host=MANAGEMENT_HOST, request_session=None):
self.requestid = None self.requestid = None
self.subscription_id = subscription_id self.subscription_id = subscription_id
self.cert_file = cert_file self.cert_file = cert_file
self.host = host self.host = host
self.request_session = request_session
self.x_ms_version = X_MS_VERSION
self.content_type = 'application/atom+xml;type=entry;charset=utf-8'
if not self.cert_file: if not self.cert_file and not request_session:
if AZURE_MANAGEMENT_CERTFILE in os.environ: if AZURE_MANAGEMENT_CERTFILE in os.environ:
self.cert_file = os.environ[AZURE_MANAGEMENT_CERTFILE] self.cert_file = os.environ[AZURE_MANAGEMENT_CERTFILE]
@@ -54,12 +57,14 @@ class _ServiceManagementClient(object):
self.subscription_id = os.environ[ self.subscription_id = os.environ[
AZURE_MANAGEMENT_SUBSCRIPTIONID] AZURE_MANAGEMENT_SUBSCRIPTIONID]
if not self.cert_file or not self.subscription_id: if not self.request_session:
raise WindowsAzureError( if not self.cert_file or not self.subscription_id:
'You need to provide subscription id and certificate file') raise WindowsAzureError(
'You need to provide subscription id and certificate file')
self._httpclient = _HTTPClient( self._httpclient = _HTTPClient(
service_instance=self, cert_file=self.cert_file) service_instance=self, cert_file=self.cert_file,
request_session=self.request_session)
self._filter = self._httpclient.perform_request self._filter = self._httpclient.perform_request
def with_filter(self, filter): def with_filter(self, filter):
@@ -69,7 +74,8 @@ class _ServiceManagementClient(object):
and another lambda. The filter can perform any pre-processing on the and another lambda. The filter can perform any pre-processing on the
request, pass it off to the next lambda, and then perform any request, pass it off to the next lambda, and then perform any
post-processing on the response.''' post-processing on the response.'''
res = type(self)(self.subscription_id, self.cert_file, self.host) res = type(self)(self.subscription_id, self.cert_file, self.host,
self.request_session)
old_filter = self._filter old_filter = self._filter
def new_filter(request): def new_filter(request):
@@ -89,6 +95,96 @@ class _ServiceManagementClient(object):
''' '''
self._httpclient.set_proxy(host, port, user, password) self._httpclient.set_proxy(host, port, user, password)
def perform_get(self, path, x_ms_version=None):
'''
Performs a GET request and returns the response.
path:
Path to the resource.
Ex: '/<subscription-id>/services/hostedservices/<service-name>'
x_ms_version:
If specified, this is used for the x-ms-version header.
Otherwise, self.x_ms_version is used.
'''
request = HTTPRequest()
request.method = 'GET'
request.host = self.host
request.path = path
request.path, request.query = _update_request_uri_query(request)
request.headers = self._update_management_header(request, x_ms_version)
response = self._perform_request(request)
return response
def perform_put(self, path, body, x_ms_version=None):
'''
Performs a PUT request and returns the response.
path:
Path to the resource.
Ex: '/<subscription-id>/services/hostedservices/<service-name>'
body:
Body for the PUT request.
x_ms_version:
If specified, this is used for the x-ms-version header.
Otherwise, self.x_ms_version is used.
'''
request = HTTPRequest()
request.method = 'PUT'
request.host = self.host
request.path = path
request.body = _get_request_body(body)
request.path, request.query = _update_request_uri_query(request)
request.headers = self._update_management_header(request, x_ms_version)
response = self._perform_request(request)
return response
def perform_post(self, path, body, x_ms_version=None):
'''
Performs a POST request and returns the response.
path:
Path to the resource.
Ex: '/<subscription-id>/services/hostedservices/<service-name>'
body:
Body for the POST request.
x_ms_version:
If specified, this is used for the x-ms-version header.
Otherwise, self.x_ms_version is used.
'''
request = HTTPRequest()
request.method = 'POST'
request.host = self.host
request.path = path
request.body = _get_request_body(body)
request.path, request.query = _update_request_uri_query(request)
request.headers = self._update_management_header(request, x_ms_version)
response = self._perform_request(request)
return response
def perform_delete(self, path, x_ms_version=None):
'''
Performs a DELETE request and returns the response.
path:
Path to the resource.
Ex: '/<subscription-id>/services/hostedservices/<service-name>'
x_ms_version:
If specified, this is used for the x-ms-version header.
Otherwise, self.x_ms_version is used.
'''
request = HTTPRequest()
request.method = 'DELETE'
request.host = self.host
request.path = path
request.path, request.query = _update_request_uri_query(request)
request.headers = self._update_management_header(request, x_ms_version)
response = self._perform_request(request)
return response
#--Helper functions -------------------------------------------------- #--Helper functions --------------------------------------------------
def _perform_request(self, request): def _perform_request(self, request):
try: try:
@@ -98,64 +194,60 @@ class _ServiceManagementClient(object):
return resp return resp
def _perform_get(self, path, response_type): def _update_management_header(self, request, x_ms_version):
request = HTTPRequest() ''' Add additional headers for management. '''
request.method = 'GET'
request.host = self.host if request.method in ['PUT', 'POST', 'MERGE', 'DELETE']:
request.path = path request.headers.append(('Content-Length', str(len(request.body))))
request.path, request.query = _update_request_uri_query(request)
request.headers = _update_management_header(request) # append additional headers base on the service
response = self._perform_request(request) request.headers.append(('x-ms-version', x_ms_version or self.x_ms_version))
# if it is not GET or HEAD request, must set content-type.
if not request.method in ['GET', 'HEAD']:
for name, _ in request.headers:
if 'content-type' == name.lower():
break
else:
request.headers.append(
('Content-Type',
self.content_type))
return request.headers
def _perform_get(self, path, response_type, x_ms_version=None):
response = self.perform_get(path, x_ms_version)
if response_type is not None: if response_type is not None:
return _parse_response(response, response_type) return _parse_response(response, response_type)
return response return response
def _perform_put(self, path, body, async=False): def _perform_put(self, path, body, async=False, x_ms_version=None):
request = HTTPRequest() response = self.perform_put(path, body, x_ms_version)
request.method = 'PUT'
request.host = self.host
request.path = path
request.body = _get_request_body(body)
request.path, request.query = _update_request_uri_query(request)
request.headers = _update_management_header(request)
response = self._perform_request(request)
if async: if async:
return _parse_response_for_async_op(response) return parse_response_for_async_op(response)
return None return None
def _perform_post(self, path, body, response_type=None, async=False): def _perform_post(self, path, body, response_type=None, async=False,
request = HTTPRequest() x_ms_version=None):
request.method = 'POST' response = self.perform_post(path, body, x_ms_version)
request.host = self.host
request.path = path
request.body = _get_request_body(body)
request.path, request.query = _update_request_uri_query(request)
request.headers = _update_management_header(request)
response = self._perform_request(request)
if response_type is not None: if response_type is not None:
return _parse_response(response, response_type) return _parse_response(response, response_type)
if async: if async:
return _parse_response_for_async_op(response) return parse_response_for_async_op(response)
return None return None
def _perform_delete(self, path, async=False): def _perform_delete(self, path, async=False, x_ms_version=None):
request = HTTPRequest() response = self.perform_delete(path, x_ms_version)
request.method = 'DELETE'
request.host = self.host
request.path = path
request.path, request.query = _update_request_uri_query(request)
request.headers = _update_management_header(request)
response = self._perform_request(request)
if async: if async:
return _parse_response_for_async_op(response) return parse_response_for_async_op(response)
return None return None

View File

@@ -37,12 +37,19 @@ from azure.servicemanagement import (
OperatingSystemFamilies, OperatingSystemFamilies,
OSImage, OSImage,
PersistentVMRole, PersistentVMRole,
ResourceExtensions,
ReservedIP,
ReservedIPs,
RoleSize,
RoleSizes,
StorageService, StorageService,
StorageServices, StorageServices,
Subscription, Subscription,
Subscriptions,
SubscriptionCertificate, SubscriptionCertificate,
SubscriptionCertificates, SubscriptionCertificates,
VirtualNetworkSites, VirtualNetworkSites,
VMImages,
_XmlSerializer, _XmlSerializer,
) )
from azure.servicemanagement.servicemanagementclient import ( from azure.servicemanagement.servicemanagementclient import (
@@ -52,9 +59,49 @@ from azure.servicemanagement.servicemanagementclient import (
class ServiceManagementService(_ServiceManagementClient): class ServiceManagementService(_ServiceManagementClient):
def __init__(self, subscription_id=None, cert_file=None, def __init__(self, subscription_id=None, cert_file=None,
host=MANAGEMENT_HOST): host=MANAGEMENT_HOST, request_session=None):
'''
Initializes the management service.
subscription_id: Subscription to manage.
cert_file:
Path to .pem certificate file (httplib), or location of the
certificate in your Personal certificate store (winhttp) in the
CURRENT_USER\my\CertificateName format.
If a request_session is specified, then this is unused.
host: Live ServiceClient URL. Defaults to Azure public cloud.
request_session:
Session object to use for http requests. If this is specified, it
replaces the default use of httplib or winhttp. Also, the cert_file
parameter is unused when a session is passed in.
The session object handles authentication, and as such can support
multiple types of authentication: .pem certificate, oauth.
For example, you can pass in a Session instance from the requests
library. To use .pem certificate authentication with requests
library, set the path to the .pem file on the session.cert
attribute.
'''
super(ServiceManagementService, self).__init__( super(ServiceManagementService, self).__init__(
subscription_id, cert_file, host) subscription_id, cert_file, host, request_session)
#--Operations for subscriptions --------------------------------------
def list_role_sizes(self):
'''
Lists the role sizes that are available under the specified
subscription.
'''
return self._perform_get(self._get_role_sizes_path(),
RoleSizes)
def list_subscriptions(self):
'''
Returns a list of subscriptions that you can access.
You must make sure that the request that is made to the management
service is secure using an Active Directory access token.
'''
return self._perform_get(self._get_subscriptions_path(),
Subscriptions)
#--Operations for storage accounts ----------------------------------- #--Operations for storage accounts -----------------------------------
def list_storage_accounts(self): def list_storage_accounts(self):
@@ -107,8 +154,9 @@ class ServiceManagementService(_ServiceManagementClient):
def create_storage_account(self, service_name, description, label, def create_storage_account(self, service_name, description, label,
affinity_group=None, location=None, affinity_group=None, location=None,
geo_replication_enabled=True, geo_replication_enabled=None,
extended_properties=None): extended_properties=None,
account_type='Standard_GRS'):
''' '''
Creates a new storage account in Windows Azure. Creates a new storage account in Windows Azure.
@@ -131,12 +179,7 @@ class ServiceManagementService(_ServiceManagementClient):
The location where the storage account is created. You can specify The location where the storage account is created. You can specify
either a location or affinity_group, but not both. either a location or affinity_group, but not both.
geo_replication_enabled: geo_replication_enabled:
Specifies whether the storage account is created with the Deprecated. Replaced by the account_type parameter.
geo-replication enabled. If the element is not included in the
request body, the default value is true. If set to true, the data
in the storage account is replicated across more than one
geographic location so as to enable resilience in the face of
catastrophic service loss.
extended_properties: extended_properties:
Dictionary containing name/value pairs of storage account Dictionary containing name/value pairs of storage account
properties. You can have a maximum of 50 extended property properties. You can have a maximum of 50 extended property
@@ -144,6 +187,12 @@ class ServiceManagementService(_ServiceManagementClient):
characters, only alphanumeric characters and underscores are valid characters, only alphanumeric characters and underscores are valid
in the Name, and the name must start with a letter. The value has in the Name, and the name must start with a letter. The value has
a maximum length of 255 characters. a maximum length of 255 characters.
account_type:
Specifies whether the account supports locally-redundant storage,
geo-redundant storage, zone-redundant storage, or read access
geo-redundant storage.
Possible values are:
Standard_LRS, Standard_ZRS, Standard_GRS, Standard_RAGRS
''' '''
_validate_not_none('service_name', service_name) _validate_not_none('service_name', service_name)
_validate_not_none('description', description) _validate_not_none('description', description)
@@ -154,6 +203,8 @@ class ServiceManagementService(_ServiceManagementClient):
if affinity_group is not None and location is not None: if affinity_group is not None and location is not None:
raise WindowsAzureError( raise WindowsAzureError(
'Only one of location or affinity_group needs to be specified') 'Only one of location or affinity_group needs to be specified')
if geo_replication_enabled == False:
account_type = 'Standard_LRS'
return self._perform_post( return self._perform_post(
self._get_storage_service_path(), self._get_storage_service_path(),
_XmlSerializer.create_storage_service_input_to_xml( _XmlSerializer.create_storage_service_input_to_xml(
@@ -162,13 +213,14 @@ class ServiceManagementService(_ServiceManagementClient):
label, label,
affinity_group, affinity_group,
location, location,
geo_replication_enabled, account_type,
extended_properties), extended_properties),
async=True) async=True)
def update_storage_account(self, service_name, description=None, def update_storage_account(self, service_name, description=None,
label=None, geo_replication_enabled=None, label=None, geo_replication_enabled=None,
extended_properties=None): extended_properties=None,
account_type='Standard_GRS'):
''' '''
Updates the label, the description, and enables or disables the Updates the label, the description, and enables or disables the
geo-replication status for a storage account in Windows Azure. geo-replication status for a storage account in Windows Azure.
@@ -182,12 +234,7 @@ class ServiceManagementService(_ServiceManagementClient):
characters in length. The name can be used to identify the storage characters in length. The name can be used to identify the storage
account for your tracking purposes. account for your tracking purposes.
geo_replication_enabled: geo_replication_enabled:
Specifies whether the storage account is created with the Deprecated. Replaced by the account_type parameter.
geo-replication enabled. If the element is not included in the
request body, the default value is true. If set to true, the data
in the storage account is replicated across more than one
geographic location so as to enable resilience in the face of
catastrophic service loss.
extended_properties: extended_properties:
Dictionary containing name/value pairs of storage account Dictionary containing name/value pairs of storage account
properties. You can have a maximum of 50 extended property properties. You can have a maximum of 50 extended property
@@ -195,14 +242,22 @@ class ServiceManagementService(_ServiceManagementClient):
characters, only alphanumeric characters and underscores are valid characters, only alphanumeric characters and underscores are valid
in the Name, and the name must start with a letter. The value has in the Name, and the name must start with a letter. The value has
a maximum length of 255 characters. a maximum length of 255 characters.
account_type:
Specifies whether the account supports locally-redundant storage,
geo-redundant storage, zone-redundant storage, or read access
geo-redundant storage.
Possible values are:
Standard_LRS, Standard_ZRS, Standard_GRS, Standard_RAGRS
''' '''
_validate_not_none('service_name', service_name) _validate_not_none('service_name', service_name)
if geo_replication_enabled == False:
account_type = 'Standard_LRS'
return self._perform_put( return self._perform_put(
self._get_storage_service_path(service_name), self._get_storage_service_path(service_name),
_XmlSerializer.update_storage_service_input_to_xml( _XmlSerializer.update_storage_service_input_to_xml(
description, description,
label, label,
geo_replication_enabled, account_type,
extended_properties)) extended_properties))
def delete_storage_account(self, service_name): def delete_storage_account(self, service_name):
@@ -697,6 +752,50 @@ class ServiceManagementService(_ServiceManagementClient):
'', '',
async=True) async=True)
def rebuild_role_instance(self, service_name, deployment_name,
role_instance_name):
'''
Reinstalls the operating system on instances of web roles or worker
roles and initializes the storage resources that are used by them. If
you do not want to initialize storage resources, you can use
reimage_role_instance.
service_name: Name of the hosted service.
deployment_name: The name of the deployment.
role_instance_name: The name of the role instance.
'''
_validate_not_none('service_name', service_name)
_validate_not_none('deployment_name', deployment_name)
_validate_not_none('role_instance_name', role_instance_name)
return self._perform_post(
self._get_deployment_path_using_name(
service_name, deployment_name) + \
'/roleinstances/' + _str(role_instance_name) + \
'?comp=rebuild&resources=allLocalDrives',
'',
async=True)
def delete_role_instances(self, service_name, deployment_name,
role_instance_names):
'''
Reinstalls the operating system on instances of web roles or worker
roles and initializes the storage resources that are used by them. If
you do not want to initialize storage resources, you can use
reimage_role_instance.
service_name: Name of the hosted service.
deployment_name: The name of the deployment.
role_instance_names: List of role instance names.
'''
_validate_not_none('service_name', service_name)
_validate_not_none('deployment_name', deployment_name)
_validate_not_none('role_instance_names', role_instance_names)
return self._perform_post(
self._get_deployment_path_using_name(
service_name, deployment_name) + '/roleinstances/?comp=delete',
_XmlSerializer.role_instances_to_xml(role_instance_names),
async=True)
def check_hosted_service_name_availability(self, service_name): def check_hosted_service_name_availability(self, service_name):
''' '''
Checks to see if the specified hosted service name is available, or if Checks to see if the specified hosted service name is available, or if
@@ -980,6 +1079,53 @@ class ServiceManagementService(_ServiceManagementClient):
return self._perform_get('/' + self.subscription_id + '', return self._perform_get('/' + self.subscription_id + '',
Subscription) Subscription)
#--Operations for reserved ip addresses -----------------------------
def create_reserved_ip_address(self, name, label=None, location=None):
'''
Reserves an IPv4 address for the specified subscription.
name:
Required. Specifies the name for the reserved IP address.
label:
Optional. Specifies a label for the reserved IP address. The label
can be up to 100 characters long and can be used for your tracking
purposes.
location:
Required. Specifies the location of the reserved IP address. This
should be the same location that is assigned to the cloud service
containing the deployment that will use the reserved IP address.
To see the available locations, you can use list_locations.
'''
_validate_not_none('name', name)
return self._perform_post(
self._get_reserved_ip_path(),
_XmlSerializer.create_reserved_ip_to_xml(name, label, location))
def delete_reserved_ip_address(self, name):
'''
Deletes a reserved IP address from the specified subscription.
name: Required. Name of the reserved IP address.
'''
_validate_not_none('name', name)
return self._perform_delete(self._get_reserved_ip_path(name))
def get_reserved_ip_address(self, name):
'''
Retrieves information about the specified reserved IP address.
name: Required. Name of the reserved IP address.
'''
_validate_not_none('name', name)
return self._perform_get(self._get_reserved_ip_path(name), ReservedIP)
def list_reserved_ip_addresses(self):
'''
Lists the IP addresses that have been reserved for the specified
subscription.
'''
return self._perform_get(self._get_reserved_ip_path(), ReservedIPs)
#--Operations for virtual machines ----------------------------------- #--Operations for virtual machines -----------------------------------
def get_role(self, service_name, deployment_name, role_name): def get_role(self, service_name, deployment_name, role_name):
''' '''
@@ -1004,7 +1150,13 @@ class ServiceManagementService(_ServiceManagementClient):
data_virtual_hard_disks=None, data_virtual_hard_disks=None,
role_size=None, role_size=None,
role_type='PersistentVMRole', role_type='PersistentVMRole',
virtual_network_name=None): virtual_network_name=None,
resource_extension_references=None,
provision_guest_agent=None,
vm_image_name=None,
media_location=None,
dns_servers=None,
reserved_ip_name=None):
''' '''
Provisions a virtual machine based on the supplied configuration. Provisions a virtual machine based on the supplied configuration.
@@ -1025,7 +1177,8 @@ class ServiceManagementService(_ServiceManagementClient):
WindowsConfigurationSet or LinuxConfigurationSet. WindowsConfigurationSet or LinuxConfigurationSet.
os_virtual_hard_disk: os_virtual_hard_disk:
Contains the parameters Windows Azure uses to create the operating Contains the parameters Windows Azure uses to create the operating
system disk for the virtual machine. system disk for the virtual machine. If you are creating a Virtual
Machine by using a VM Image, this parameter is not used.
network_config: network_config:
Encapsulates the metadata required to create the virtual network Encapsulates the metadata required to create the virtual network
configuration for a virtual machine. If you do not include a configuration for a virtual machine. If you do not include a
@@ -1053,14 +1206,36 @@ class ServiceManagementService(_ServiceManagementClient):
virtual_network_name: virtual_network_name:
Specifies the name of an existing virtual network to which the Specifies the name of an existing virtual network to which the
deployment will belong. deployment will belong.
resource_extension_references:
Optional. Contains a collection of resource extensions that are to
be installed on the Virtual Machine. This element is used if
provision_guest_agent is set to True.
provision_guest_agent:
Optional. Indicates whether the VM Agent is installed on the
Virtual Machine. To run a resource extension in a Virtual Machine,
this service must be installed.
vm_image_name:
Optional. Specifies the name of the VM Image that is to be used to
create the Virtual Machine. If this is specified, the
system_config and network_config parameters are not used.
media_location:
Optional. Required if the Virtual Machine is being created from a
published VM Image. Specifies the location of the VHD file that is
created when VMImageName specifies a published VM Image.
dns_servers:
Optional. List of DNS servers (use DnsServer class) to associate
with the Virtual Machine.
reserved_ip_name:
Optional. Specifies the name of a reserved IP address that is to be
assigned to the deployment. You must run create_reserved_ip_address
before you can assign the address to the deployment using this
element.
''' '''
_validate_not_none('service_name', service_name) _validate_not_none('service_name', service_name)
_validate_not_none('deployment_name', deployment_name) _validate_not_none('deployment_name', deployment_name)
_validate_not_none('deployment_slot', deployment_slot) _validate_not_none('deployment_slot', deployment_slot)
_validate_not_none('label', label) _validate_not_none('label', label)
_validate_not_none('role_name', role_name) _validate_not_none('role_name', role_name)
_validate_not_none('system_config', system_config)
_validate_not_none('os_virtual_hard_disk', os_virtual_hard_disk)
return self._perform_post( return self._perform_post(
self._get_deployment_path_using_name(service_name), self._get_deployment_path_using_name(service_name),
_XmlSerializer.virtual_machine_deployment_to_xml( _XmlSerializer.virtual_machine_deployment_to_xml(
@@ -1075,13 +1250,22 @@ class ServiceManagementService(_ServiceManagementClient):
availability_set_name, availability_set_name,
data_virtual_hard_disks, data_virtual_hard_disks,
role_size, role_size,
virtual_network_name), virtual_network_name,
resource_extension_references,
provision_guest_agent,
vm_image_name,
media_location,
dns_servers,
reserved_ip_name),
async=True) async=True)
def add_role(self, service_name, deployment_name, role_name, system_config, def add_role(self, service_name, deployment_name, role_name, system_config,
os_virtual_hard_disk, network_config=None, os_virtual_hard_disk, network_config=None,
availability_set_name=None, data_virtual_hard_disks=None, availability_set_name=None, data_virtual_hard_disks=None,
role_size=None, role_type='PersistentVMRole'): role_size=None, role_type='PersistentVMRole',
resource_extension_references=None,
provision_guest_agent=None, vm_image_name=None,
media_location=None):
''' '''
Adds a virtual machine to an existing deployment. Adds a virtual machine to an existing deployment.
@@ -1094,7 +1278,8 @@ class ServiceManagementService(_ServiceManagementClient):
WindowsConfigurationSet or LinuxConfigurationSet. WindowsConfigurationSet or LinuxConfigurationSet.
os_virtual_hard_disk: os_virtual_hard_disk:
Contains the parameters Windows Azure uses to create the operating Contains the parameters Windows Azure uses to create the operating
system disk for the virtual machine. system disk for the virtual machine. If you are creating a Virtual
Machine by using a VM Image, this parameter is not used.
network_config: network_config:
Encapsulates the metadata required to create the virtual network Encapsulates the metadata required to create the virtual network
configuration for a virtual machine. If you do not include a configuration for a virtual machine. If you do not include a
@@ -1119,12 +1304,26 @@ class ServiceManagementService(_ServiceManagementClient):
role_type: role_type:
The type of the role for the virtual machine. The only supported The type of the role for the virtual machine. The only supported
value is PersistentVMRole. value is PersistentVMRole.
resource_extension_references:
Optional. Contains a collection of resource extensions that are to
be installed on the Virtual Machine. This element is used if
provision_guest_agent is set to True.
provision_guest_agent:
Optional. Indicates whether the VM Agent is installed on the
Virtual Machine. To run a resource extension in a Virtual Machine,
this service must be installed.
vm_image_name:
Optional. Specifies the name of the VM Image that is to be used to
create the Virtual Machine. If this is specified, the
system_config and network_config parameters are not used.
media_location:
Optional. Required if the Virtual Machine is being created from a
published VM Image. Specifies the location of the VHD file that is
created when VMImageName specifies a published VM Image.
''' '''
_validate_not_none('service_name', service_name) _validate_not_none('service_name', service_name)
_validate_not_none('deployment_name', deployment_name) _validate_not_none('deployment_name', deployment_name)
_validate_not_none('role_name', role_name) _validate_not_none('role_name', role_name)
_validate_not_none('system_config', system_config)
_validate_not_none('os_virtual_hard_disk', os_virtual_hard_disk)
return self._perform_post( return self._perform_post(
self._get_role_path(service_name, deployment_name), self._get_role_path(service_name, deployment_name),
_XmlSerializer.add_role_to_xml( _XmlSerializer.add_role_to_xml(
@@ -1135,13 +1334,19 @@ class ServiceManagementService(_ServiceManagementClient):
network_config, network_config,
availability_set_name, availability_set_name,
data_virtual_hard_disks, data_virtual_hard_disks,
role_size), role_size,
resource_extension_references,
provision_guest_agent,
vm_image_name,
media_location),
async=True) async=True)
def update_role(self, service_name, deployment_name, role_name, def update_role(self, service_name, deployment_name, role_name,
os_virtual_hard_disk=None, network_config=None, os_virtual_hard_disk=None, network_config=None,
availability_set_name=None, data_virtual_hard_disks=None, availability_set_name=None, data_virtual_hard_disks=None,
role_size=None, role_type='PersistentVMRole'): role_size=None, role_type='PersistentVMRole',
resource_extension_references=None,
provision_guest_agent=None):
''' '''
Updates the specified virtual machine. Updates the specified virtual machine.
@@ -1175,6 +1380,14 @@ class ServiceManagementService(_ServiceManagementClient):
role_type: role_type:
The type of the role for the virtual machine. The only supported The type of the role for the virtual machine. The only supported
value is PersistentVMRole. value is PersistentVMRole.
resource_extension_references:
Optional. Contains a collection of resource extensions that are to
be installed on the Virtual Machine. This element is used if
provision_guest_agent is set to True.
provision_guest_agent:
Optional. Indicates whether the VM Agent is installed on the
Virtual Machine. To run a resource extension in a Virtual Machine,
this service must be installed.
''' '''
_validate_not_none('service_name', service_name) _validate_not_none('service_name', service_name)
_validate_not_none('deployment_name', deployment_name) _validate_not_none('deployment_name', deployment_name)
@@ -1188,7 +1401,9 @@ class ServiceManagementService(_ServiceManagementClient):
network_config, network_config,
availability_set_name, availability_set_name,
data_virtual_hard_disks, data_virtual_hard_disks,
role_size), role_size,
resource_extension_references,
provision_guest_agent),
async=True) async=True)
def delete_role(self, service_name, deployment_name, role_name): def delete_role(self, service_name, deployment_name, role_name):
@@ -1354,7 +1569,307 @@ class ServiceManagementService(_ServiceManagementClient):
role_names, post_shutdown_action), role_names, post_shutdown_action),
async=True) async=True)
def add_dns_server(self, service_name, deployment_name, dns_server_name, address):
'''
Adds a DNS server definition to an existing deployment.
service_name: The name of the service.
deployment_name: The name of the deployment.
dns_server_name: Specifies the name of the DNS server.
address: Specifies the IP address of the DNS server.
'''
_validate_not_none('service_name', service_name)
_validate_not_none('deployment_name', deployment_name)
_validate_not_none('dns_server_name', dns_server_name)
_validate_not_none('address', address)
return self._perform_post(
self._get_dns_server_path(service_name, deployment_name),
_XmlSerializer.dns_server_to_xml(dns_server_name, address),
async=True)
def update_dns_server(self, service_name, deployment_name, dns_server_name, address):
'''
Updates the ip address of a DNS server.
service_name: The name of the service.
deployment_name: The name of the deployment.
dns_server_name: Specifies the name of the DNS server.
address: Specifies the IP address of the DNS server.
'''
_validate_not_none('service_name', service_name)
_validate_not_none('deployment_name', deployment_name)
_validate_not_none('dns_server_name', dns_server_name)
_validate_not_none('address', address)
return self._perform_put(
self._get_dns_server_path(service_name,
deployment_name,
dns_server_name),
_XmlSerializer.dns_server_to_xml(dns_server_name, address),
async=True)
def delete_dns_server(self, service_name, deployment_name, dns_server_name):
'''
Deletes a DNS server from a deployment.
service_name: The name of the service.
deployment_name: The name of the deployment.
dns_server_name: Name of the DNS server that you want to delete.
'''
_validate_not_none('service_name', service_name)
_validate_not_none('deployment_name', deployment_name)
_validate_not_none('dns_server_name', dns_server_name)
return self._perform_delete(
self._get_dns_server_path(service_name,
deployment_name,
dns_server_name),
async=True)
def list_resource_extensions(self):
'''
Lists the resource extensions that are available to add to a
Virtual Machine.
'''
return self._perform_get(self._get_resource_extensions_path(),
ResourceExtensions)
def list_resource_extension_versions(self, publisher_name, extension_name):
'''
Lists the versions of a resource extension that are available to add
to a Virtual Machine.
publisher_name: Name of the resource extension publisher.
extension_name: Name of the resource extension.
'''
return self._perform_get(self._get_resource_extension_versions_path(
publisher_name, extension_name),
ResourceExtensions)
#--Operations for virtual machine images ----------------------------- #--Operations for virtual machine images -----------------------------
def capture_vm_image(self, service_name, deployment_name, role_name, options):
'''
Creates a copy of the operating system virtual hard disk (VHD) and all
of the data VHDs that are associated with the Virtual Machine, saves
the VHD copies in the same storage location as the original VHDs, and
registers the copies as a VM Image in the image repository that is
associated with the specified subscription.
service_name: The name of the service.
deployment_name: The name of the deployment.
role_name: The name of the role.
options: An instance of CaptureRoleAsVMImage class.
options.os_state:
Required. Specifies the state of the operating system in the image.
Possible values are: Generalized, Specialized
A Virtual Machine that is fully configured and running contains a
Specialized operating system. A Virtual Machine on which the
Sysprep command has been run with the generalize option contains a
Generalized operating system. If you capture an image from a
generalized Virtual Machine, the machine is deleted after the image
is captured. It is recommended that all Virtual Machines are shut
down before capturing an image.
options.vm_image_name:
Required. Specifies the name of the VM Image.
options.vm_image_name:
Required. Specifies the label of the VM Image.
options.description:
Optional. Specifies the description of the VM Image.
options.language:
Optional. Specifies the language of the VM Image.
options.image_family:
Optional. Specifies a value that can be used to group VM Images.
options.recommended_vm_size:
Optional. Specifies the size to use for the Virtual Machine that
is created from the VM Image.
'''
_validate_not_none('service_name', service_name)
_validate_not_none('deployment_name', deployment_name)
_validate_not_none('role_name', role_name)
_validate_not_none('options', options)
_validate_not_none('options.os_state', options.os_state)
_validate_not_none('options.vm_image_name', options.vm_image_name)
_validate_not_none('options.vm_image_label', options.vm_image_label)
return self._perform_post(
self._get_capture_vm_image_path(service_name, deployment_name, role_name),
_XmlSerializer.capture_vm_image_to_xml(options),
async=True)
def create_vm_image(self, vm_image):
'''
Creates a VM Image in the image repository that is associated with the
specified subscription using a specified set of virtual hard disks.
vm_image: An instance of VMImage class.
vm_image.name: Required. Specifies the name of the image.
vm_image.label: Required. Specifies an identifier for the image.
vm_image.description: Optional. Specifies the description of the image.
vm_image.os_disk_configuration:
Required. Specifies configuration information for the operating
system disk that is associated with the image.
vm_image.os_disk_configuration.host_caching:
Optional. Specifies the caching behavior of the operating system disk.
Possible values are: None, ReadOnly, ReadWrite
vm_image.os_disk_configuration.os_state:
Required. Specifies the state of the operating system in the image.
Possible values are: Generalized, Specialized
A Virtual Machine that is fully configured and running contains a
Specialized operating system. A Virtual Machine on which the
Sysprep command has been run with the generalize option contains a
Generalized operating system.
vm_image.os_disk_configuration.os:
Required. Specifies the operating system type of the image.
vm_image.os_disk_configuration.media_link:
Required. Specifies the location of the blob in Windows Azure
storage. The blob location belongs to a storage account in the
subscription specified by the <subscription-id> value in the
operation call.
vm_image.data_disk_configurations:
Optional. Specifies configuration information for the data disks
that are associated with the image. A VM Image might not have data
disks associated with it.
vm_image.data_disk_configurations[].host_caching:
Optional. Specifies the caching behavior of the data disk.
Possible values are: None, ReadOnly, ReadWrite
vm_image.data_disk_configurations[].lun:
Optional if the lun for the disk is 0. Specifies the Logical Unit
Number (LUN) for the data disk.
vm_image.data_disk_configurations[].media_link:
Required. Specifies the location of the blob in Windows Azure
storage. The blob location belongs to a storage account in the
subscription specified by the <subscription-id> value in the
operation call.
vm_image.data_disk_configurations[].logical_size_in_gb:
Required. Specifies the size, in GB, of the data disk.
vm_image.language: Optional. Specifies the language of the image.
vm_image.image_family:
Optional. Specifies a value that can be used to group VM Images.
vm_image.recommended_vm_size:
Optional. Specifies the size to use for the Virtual Machine that
is created from the VM Image.
vm_image.eula:
Optional. Specifies the End User License Agreement that is
associated with the image. The value for this element is a string,
but it is recommended that the value be a URL that points to a EULA.
vm_image.icon_uri:
Optional. Specifies the URI to the icon that is displayed for the
image in the Management Portal.
vm_image.small_icon_uri:
Optional. Specifies the URI to the small icon that is displayed for
the image in the Management Portal.
vm_image.privacy_uri:
Optional. Specifies the URI that points to a document that contains
the privacy policy related to the image.
vm_image.published_date:
Optional. Specifies the date when the image was added to the image
repository.
vm_image.show_in_gui:
Optional. Indicates whether the VM Images should be listed in the
portal.
'''
_validate_not_none('vm_image', vm_image)
_validate_not_none('vm_image.name', vm_image.name)
_validate_not_none('vm_image.label', vm_image.label)
_validate_not_none('vm_image.os_disk_configuration.os_state',
vm_image.os_disk_configuration.os_state)
_validate_not_none('vm_image.os_disk_configuration.os',
vm_image.os_disk_configuration.os)
_validate_not_none('vm_image.os_disk_configuration.media_link',
vm_image.os_disk_configuration.media_link)
return self._perform_post(
self._get_vm_image_path(),
_XmlSerializer.create_vm_image_to_xml(vm_image),
async=True)
def delete_vm_image(self, vm_image_name, delete_vhd=False):
'''
Deletes the specified VM Image from the image repository that is
associated with the specified subscription.
vm_image_name: The name of the image.
delete_vhd: Deletes the underlying vhd blob in Azure storage.
'''
_validate_not_none('vm_image_name', vm_image_name)
path = self._get_vm_image_path(vm_image_name)
if delete_vhd:
path += '?comp=media'
return self._perform_delete(path, async=True)
def list_vm_images(self, location=None, publisher=None, category=None):
'''
Retrieves a list of the VM Images from the image repository that is
associated with the specified subscription.
'''
path = self._get_vm_image_path()
query = ''
if location:
query += '&location=' + location
if publisher:
query += '&publisher=' + publisher
if category:
query += '&category=' + category
if query:
path = path + '?' + query.lstrip('&')
return self._perform_get(path, VMImages)
def update_vm_image(self, vm_image_name, vm_image):
'''
Updates a VM Image in the image repository that is associated with the
specified subscription.
vm_image_name: Name of image to update.
vm_image: An instance of VMImage class.
vm_image.label: Optional. Specifies an identifier for the image.
vm_image.os_disk_configuration:
Required. Specifies configuration information for the operating
system disk that is associated with the image.
vm_image.os_disk_configuration.host_caching:
Optional. Specifies the caching behavior of the operating system disk.
Possible values are: None, ReadOnly, ReadWrite
vm_image.data_disk_configurations:
Optional. Specifies configuration information for the data disks
that are associated with the image. A VM Image might not have data
disks associated with it.
vm_image.data_disk_configurations[].name:
Required. Specifies the name of the data disk.
vm_image.data_disk_configurations[].host_caching:
Optional. Specifies the caching behavior of the data disk.
Possible values are: None, ReadOnly, ReadWrite
vm_image.data_disk_configurations[].lun:
Optional if the lun for the disk is 0. Specifies the Logical Unit
Number (LUN) for the data disk.
vm_image.description: Optional. Specifies the description of the image.
vm_image.language: Optional. Specifies the language of the image.
vm_image.image_family:
Optional. Specifies a value that can be used to group VM Images.
vm_image.recommended_vm_size:
Optional. Specifies the size to use for the Virtual Machine that
is created from the VM Image.
vm_image.eula:
Optional. Specifies the End User License Agreement that is
associated with the image. The value for this element is a string,
but it is recommended that the value be a URL that points to a EULA.
vm_image.icon_uri:
Optional. Specifies the URI to the icon that is displayed for the
image in the Management Portal.
vm_image.small_icon_uri:
Optional. Specifies the URI to the small icon that is displayed for
the image in the Management Portal.
vm_image.privacy_uri:
Optional. Specifies the URI that points to a document that contains
the privacy policy related to the image.
vm_image.published_date:
Optional. Specifies the date when the image was added to the image
repository.
vm_image.show_in_gui:
Optional. Indicates whether the VM Images should be listed in the
portal.
'''
_validate_not_none('vm_image_name', vm_image_name)
_validate_not_none('vm_image', vm_image)
return self._perform_put(self._get_vm_image_path(vm_image_name),
_XmlSerializer.update_vm_image_to_xml(vm_image),
async=True)
#--Operations for operating system images ----------------------------
def list_os_images(self): def list_os_images(self):
''' '''
Retrieves a list of the OS images from the image repository. Retrieves a list of the OS images from the image repository.
@@ -1707,6 +2222,12 @@ class ServiceManagementService(_ServiceManagementClient):
return self._perform_get(self._get_virtual_network_site_path(), VirtualNetworkSites) return self._perform_get(self._get_virtual_network_site_path(), VirtualNetworkSites)
#--Helper functions -------------------------------------------------- #--Helper functions --------------------------------------------------
def _get_role_sizes_path(self):
return self._get_path('rolesizes', None)
def _get_subscriptions_path(self):
return '/subscriptions'
def _get_virtual_network_site_path(self): def _get_virtual_network_site_path(self):
return self._get_path('services/networking/virtualnetwork', None) return self._get_path('services/networking/virtualnetwork', None)
@@ -1741,6 +2262,31 @@ class ServiceManagementService(_ServiceManagementClient):
'/deployments/' + deployment_name + '/deployments/' + deployment_name +
'/roles/Operations', None) '/roles/Operations', None)
def _get_resource_extensions_path(self):
return self._get_path('services/resourceextensions', None)
def _get_resource_extension_versions_path(self, publisher_name, extension_name):
return self._get_path('services/resourceextensions',
publisher_name + '/' + extension_name)
def _get_dns_server_path(self, service_name, deployment_name,
dns_server_name=None):
return self._get_path('services/hostedservices/' + _str(service_name) +
'/deployments/' + deployment_name +
'/dnsservers', dns_server_name)
def _get_capture_vm_image_path(self, service_name, deployment_name, role_name):
return self._get_path('services/hostedservices/' + _str(service_name) +
'/deployments/' + _str(deployment_name) +
'/roleinstances/' + _str(role_name) + '/Operations',
None)
def _get_vm_image_path(self, image_name=None):
return self._get_path('services/vmimages', image_name)
def _get_reserved_ip_path(self, name=None):
return self._get_path('services/networking/reservedips', name)
def _get_data_disk_path(self, service_name, deployment_name, role_name, def _get_data_disk_path(self, service_name, deployment_name, role_name,
lun=None): lun=None):
return self._get_path('services/hostedservices/' + _str(service_name) + return self._get_path('services/hostedservices/' + _str(service_name) +

View File

@@ -0,0 +1,390 @@
#-------------------------------------------------------------------------
# Copyright (c) Microsoft. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#--------------------------------------------------------------------------
from azure import (
MANAGEMENT_HOST,
_parse_service_resources_response,
_validate_not_none,
)
from azure.servicemanagement import (
EventLog,
ServerQuota,
Servers,
ServiceObjective,
Database,
FirewallRule,
_SqlManagementXmlSerializer,
)
from azure.servicemanagement.servicemanagementclient import (
_ServiceManagementClient,
)
class SqlDatabaseManagementService(_ServiceManagementClient):
''' Note that this class is a preliminary work on SQL Database
management. Since it lack a lot a features, final version
can be slightly different from the current one.
'''
def __init__(self, subscription_id=None, cert_file=None,
host=MANAGEMENT_HOST, request_session=None):
'''
Initializes the sql database management service.
subscription_id: Subscription to manage.
cert_file:
Path to .pem certificate file (httplib), or location of the
certificate in your Personal certificate store (winhttp) in the
CURRENT_USER\my\CertificateName format.
If a request_session is specified, then this is unused.
host: Live ServiceClient URL. Defaults to Azure public cloud.
request_session:
Session object to use for http requests. If this is specified, it
replaces the default use of httplib or winhttp. Also, the cert_file
parameter is unused when a session is passed in.
The session object handles authentication, and as such can support
multiple types of authentication: .pem certificate, oauth.
For example, you can pass in a Session instance from the requests
library. To use .pem certificate authentication with requests
library, set the path to the .pem file on the session.cert
attribute.
'''
super(SqlDatabaseManagementService, self).__init__(
subscription_id, cert_file, host, request_session)
self.content_type = 'application/xml'
#--Operations for sql servers ----------------------------------------
def create_server(self, admin_login, admin_password, location):
'''
Create a new Azure SQL Database server.
admin_login: The administrator login name for the new server.
admin_password: The administrator login password for the new server.
location: The region to deploy the new server.
'''
_validate_not_none('admin_login', admin_login)
_validate_not_none('admin_password', admin_password)
_validate_not_none('location', location)
response = self.perform_post(
self._get_servers_path(),
_SqlManagementXmlSerializer.create_server_to_xml(
admin_login,
admin_password,
location
)
)
return _SqlManagementXmlSerializer.xml_to_create_server_response(
response.body)
def set_server_admin_password(self, server_name, admin_password):
'''
Reset the administrator password for a server.
server_name: Name of the server to change the password.
admin_password: The new administrator password for the server.
'''
_validate_not_none('server_name', server_name)
_validate_not_none('admin_password', admin_password)
return self._perform_post(
self._get_servers_path(server_name) + '?op=ResetPassword',
_SqlManagementXmlSerializer.set_server_admin_password_to_xml(
admin_password
)
)
def delete_server(self, server_name):
'''
Deletes an Azure SQL Database server (including all its databases).
server_name: Name of the server you want to delete.
'''
_validate_not_none('server_name', server_name)
return self._perform_delete(
self._get_servers_path(server_name))
def list_servers(self):
'''
List the SQL servers defined on the account.
'''
return self._perform_get(self._get_servers_path(),
Servers)
def list_quotas(self, server_name):
'''
Gets quotas for an Azure SQL Database Server.
server_name: Name of the server.
'''
_validate_not_none('server_name', server_name)
response = self._perform_get(self._get_quotas_path(server_name),
None)
return _parse_service_resources_response(response, ServerQuota)
def get_server_event_logs(self, server_name, start_date,
interval_size_in_minutes, event_types=''):
'''
Gets the event logs for an Azure SQL Database Server.
server_name: Name of the server to retrieve the event logs from.
start_date:
The starting date and time of the events to retrieve in UTC format,
for example '2011-09-28 16:05:00'.
interval_size_in_minutes:
Size of the event logs to retrieve (in minutes).
Valid values are: 5, 60, or 1440.
event_types:
The event type of the log entries you want to retrieve.
Valid values are:
- connection_successful
- connection_failed
- connection_terminated
- deadlock
- throttling
- throttling_long_transaction
To return all event types pass in an empty string.
'''
_validate_not_none('server_name', server_name)
_validate_not_none('start_date', start_date)
_validate_not_none('interval_size_in_minutes', interval_size_in_minutes)
_validate_not_none('event_types', event_types)
path = self._get_server_event_logs_path(server_name) + \
'?startDate={0}&intervalSizeInMinutes={1}&eventTypes={2}'.format(
start_date, interval_size_in_minutes, event_types)
response = self._perform_get(path, None)
return _parse_service_resources_response(response, EventLog)
#--Operations for firewall rules ------------------------------------------
def create_firewall_rule(self, server_name, name, start_ip_address,
end_ip_address):
'''
Creates an Azure SQL Database server firewall rule.
server_name: Name of the server to set the firewall rule on.
name: The name of the new firewall rule.
start_ip_address:
The lowest IP address in the range of the server-level firewall
setting. IP addresses equal to or greater than this can attempt to
connect to the server. The lowest possible IP address is 0.0.0.0.
end_ip_address:
The highest IP address in the range of the server-level firewall
setting. IP addresses equal to or less than this can attempt to
connect to the server. The highest possible IP address is
255.255.255.255.
'''
_validate_not_none('server_name', server_name)
_validate_not_none('name', name)
_validate_not_none('start_ip_address', start_ip_address)
_validate_not_none('end_ip_address', end_ip_address)
return self._perform_post(
self._get_firewall_rules_path(server_name),
_SqlManagementXmlSerializer.create_firewall_rule_to_xml(
name, start_ip_address, end_ip_address
)
)
def update_firewall_rule(self, server_name, name, start_ip_address,
end_ip_address):
'''
Update a firewall rule for an Azure SQL Database server.
server_name: Name of the server to set the firewall rule on.
name: The name of the firewall rule to update.
start_ip_address:
The lowest IP address in the range of the server-level firewall
setting. IP addresses equal to or greater than this can attempt to
connect to the server. The lowest possible IP address is 0.0.0.0.
end_ip_address:
The highest IP address in the range of the server-level firewall
setting. IP addresses equal to or less than this can attempt to
connect to the server. The highest possible IP address is
255.255.255.255.
'''
_validate_not_none('server_name', server_name)
_validate_not_none('name', name)
_validate_not_none('start_ip_address', start_ip_address)
_validate_not_none('end_ip_address', end_ip_address)
return self._perform_put(
self._get_firewall_rules_path(server_name, name),
_SqlManagementXmlSerializer.update_firewall_rule_to_xml(
name, start_ip_address, end_ip_address
)
)
def delete_firewall_rule(self, server_name, name):
'''
Deletes an Azure SQL Database server firewall rule.
server_name:
Name of the server with the firewall rule you want to delete.
name:
Name of the firewall rule you want to delete.
'''
_validate_not_none('server_name', server_name)
_validate_not_none('name', name)
return self._perform_delete(
self._get_firewall_rules_path(server_name, name))
def list_firewall_rules(self, server_name):
'''
Retrieves the set of firewall rules for an Azure SQL Database Server.
server_name: Name of the server.
'''
_validate_not_none('server_name', server_name)
response = self._perform_get(self._get_firewall_rules_path(server_name),
None)
return _parse_service_resources_response(response, FirewallRule)
def list_service_level_objectives(self, server_name):
'''
Gets the service level objectives for an Azure SQL Database server.
server_name: Name of the server.
'''
_validate_not_none('server_name', server_name)
response = self._perform_get(
self._get_service_objectives_path(server_name), None)
return _parse_service_resources_response(response, ServiceObjective)
#--Operations for sql databases ----------------------------------------
def create_database(self, server_name, name, service_objective_id,
edition=None, collation_name=None,
max_size_bytes=None):
'''
Creates a new Azure SQL Database.
server_name: Name of the server to contain the new database.
name:
Required. The name for the new database. See Naming Requirements
in Azure SQL Database General Guidelines and Limitations and
Database Identifiers for more information.
service_objective_id:
Required. The GUID corresponding to the performance level for
Edition. See List Service Level Objectives for current values.
edition:
Optional. The Service Tier (Edition) for the new database. If
omitted, the default is Web. Valid values are Web, Business,
Basic, Standard, and Premium. See Azure SQL Database Service Tiers
(Editions) and Web and Business Edition Sunset FAQ for more
information.
collation_name:
Optional. The database collation. This can be any collation
supported by SQL. If omitted, the default collation is used. See
SQL Server Collation Support in Azure SQL Database General
Guidelines and Limitations for more information.
max_size_bytes:
Optional. Sets the maximum size, in bytes, for the database. This
value must be within the range of allowed values for Edition. If
omitted, the default value for the edition is used. See Azure SQL
Database Service Tiers (Editions) for current maximum databases
sizes. Convert MB or GB values to bytes.
1 MB = 1048576 bytes. 1 GB = 1073741824 bytes.
'''
_validate_not_none('server_name', server_name)
_validate_not_none('name', name)
_validate_not_none('service_objective_id', service_objective_id)
return self._perform_post(
self._get_databases_path(server_name),
_SqlManagementXmlSerializer.create_database_to_xml(
name, service_objective_id, edition, collation_name,
max_size_bytes
)
)
def update_database(self, server_name, name, new_database_name=None,
service_objective_id=None, edition=None,
max_size_bytes=None):
'''
Updates existing database details.
server_name: Name of the server to contain the new database.
name:
Required. The name for the new database. See Naming Requirements
in Azure SQL Database General Guidelines and Limitations and
Database Identifiers for more information.
new_database_name:
Optional. The new name for the new database.
service_objective_id:
Optional. The new service level to apply to the database. For more
information about service levels, see Azure SQL Database Service
Tiers and Performance Levels. Use List Service Level Objectives to
get the correct ID for the desired service objective.
edition:
Optional. The new edition for the new database.
max_size_bytes:
Optional. The new size of the database in bytes. For information on
available sizes for each edition, see Azure SQL Database Service
Tiers (Editions).
'''
_validate_not_none('server_name', server_name)
_validate_not_none('name', name)
return self._perform_put(
self._get_databases_path(server_name, name),
_SqlManagementXmlSerializer.update_database_to_xml(
new_database_name, service_objective_id, edition,
max_size_bytes
)
)
def delete_database(self, server_name, name):
'''
Deletes an Azure SQL Database.
server_name: Name of the server where the database is located.
name: Name of the database to delete.
'''
return self._perform_delete(self._get_databases_path(server_name, name))
def list_databases(self, name):
'''
List the SQL databases defined on the specified server name
'''
response = self._perform_get(self._get_list_databases_path(name),
None)
return _parse_service_resources_response(response, Database)
#--Helper functions --------------------------------------------------
def _get_servers_path(self, server_name=None):
return self._get_path('services/sqlservers/servers', server_name)
def _get_firewall_rules_path(self, server_name, name=None):
path = self._get_servers_path(server_name) + '/firewallrules'
if name:
path = path + '/' + name
return path
def _get_databases_path(self, server_name, name=None):
path = self._get_servers_path(server_name) + '/databases'
if name:
path = path + '/' + name
return path
def _get_server_event_logs_path(self, server_name):
return self._get_servers_path(server_name) + '/events'
def _get_service_objectives_path(self, server_name):
return self._get_servers_path(server_name) + '/serviceobjectives'
def _get_quotas_path(self, server_name, name=None):
path = self._get_servers_path(server_name) + '/serverquotas'
if name:
path = path + '/' + name
return path
def _get_list_databases_path(self, name):
# *contentview=generic is mandatory*
return self._get_path('services/sqlservers/servers/',
name) + '/databases?contentview=generic'

View File

@@ -0,0 +1,256 @@
#-------------------------------------------------------------------------
# Copyright (c) Microsoft. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#--------------------------------------------------------------------------
from azure import (
MANAGEMENT_HOST,
_str,
)
from azure.servicemanagement import (
WebSpaces,
WebSpace,
Sites,
Site,
MetricResponses,
MetricDefinitions,
PublishData,
_XmlSerializer,
)
from azure.servicemanagement.servicemanagementclient import (
_ServiceManagementClient,
)
class WebsiteManagementService(_ServiceManagementClient):
''' Note that this class is a preliminary work on WebSite
management. Since it lack a lot a features, final version
can be slightly different from the current one.
'''
def __init__(self, subscription_id=None, cert_file=None,
host=MANAGEMENT_HOST, request_session=None):
'''
Initializes the website management service.
subscription_id: Subscription to manage.
cert_file:
Path to .pem certificate file (httplib), or location of the
certificate in your Personal certificate store (winhttp) in the
CURRENT_USER\my\CertificateName format.
If a request_session is specified, then this is unused.
host: Live ServiceClient URL. Defaults to Azure public cloud.
request_session:
Session object to use for http requests. If this is specified, it
replaces the default use of httplib or winhttp. Also, the cert_file
parameter is unused when a session is passed in.
The session object handles authentication, and as such can support
multiple types of authentication: .pem certificate, oauth.
For example, you can pass in a Session instance from the requests
library. To use .pem certificate authentication with requests
library, set the path to the .pem file on the session.cert
attribute.
'''
super(WebsiteManagementService, self).__init__(
subscription_id, cert_file, host, request_session)
#--Operations for web sites ----------------------------------------
def list_webspaces(self):
'''
List the webspaces defined on the account.
'''
return self._perform_get(self._get_list_webspaces_path(),
WebSpaces)
def get_webspace(self, webspace_name):
'''
Get details of a specific webspace.
webspace_name: The name of the webspace.
'''
return self._perform_get(self._get_webspace_details_path(webspace_name),
WebSpace)
def list_sites(self, webspace_name):
'''
List the web sites defined on this webspace.
webspace_name: The name of the webspace.
'''
return self._perform_get(self._get_sites_path(webspace_name),
Sites)
def get_site(self, webspace_name, website_name):
'''
List the web sites defined on this webspace.
webspace_name: The name of the webspace.
website_name: The name of the website.
'''
return self._perform_get(self._get_sites_details_path(webspace_name,
website_name),
Site)
def create_site(self, webspace_name, website_name, geo_region, host_names,
plan='VirtualDedicatedPlan', compute_mode='Shared',
server_farm=None, site_mode=None):
'''
Create a website.
webspace_name: The name of the webspace.
website_name: The name of the website.
geo_region:
The geographical region of the webspace that will be created.
host_names:
An array of fully qualified domain names for website. Only one
hostname can be specified in the azurewebsites.net domain.
The hostname should match the name of the website. Custom domains
can only be specified for Shared or Standard websites.
plan:
This value must be 'VirtualDedicatedPlan'.
compute_mode:
This value should be 'Shared' for the Free or Paid Shared
offerings, or 'Dedicated' for the Standard offering. The default
value is 'Shared'. If you set it to 'Dedicated', you must specify
a value for the server_farm parameter.
server_farm:
The name of the Server Farm associated with this website. This is
a required value for Standard mode.
site_mode:
Can be None, 'Limited' or 'Basic'. This value is 'Limited' for the
Free offering, and 'Basic' for the Paid Shared offering. Standard
mode does not use the site_mode parameter; it uses the compute_mode
parameter.
'''
xml = _XmlSerializer.create_website_to_xml(webspace_name, website_name, geo_region, plan, host_names, compute_mode, server_farm, site_mode)
return self._perform_post(
self._get_sites_path(webspace_name),
xml,
Site)
def delete_site(self, webspace_name, website_name,
delete_empty_server_farm=False, delete_metrics=False):
'''
Delete a website.
webspace_name: The name of the webspace.
website_name: The name of the website.
delete_empty_server_farm:
If the site being deleted is the last web site in a server farm,
you can delete the server farm by setting this to True.
delete_metrics:
To also delete the metrics for the site that you are deleting, you
can set this to True.
'''
path = self._get_sites_details_path(webspace_name, website_name)
query = ''
if delete_empty_server_farm:
query += '&deleteEmptyServerFarm=true'
if delete_metrics:
query += '&deleteMetrics=true'
if query:
path = path + '?' + query.lstrip('&')
return self._perform_delete(path)
def restart_site(self, webspace_name, website_name):
'''
Restart a web site.
webspace_name: The name of the webspace.
website_name: The name of the website.
'''
return self._perform_post(
self._get_restart_path(webspace_name, website_name),
'')
def get_historical_usage_metrics(self, webspace_name, website_name,
metrics = None, start_time=None, end_time=None, time_grain=None):
'''
Get historical usage metrics.
webspace_name: The name of the webspace.
website_name: The name of the website.
metrics: Optional. List of metrics name. Otherwise, all metrics returned.
start_time: Optional. An ISO8601 date. Otherwise, current hour is used.
end_time: Optional. An ISO8601 date. Otherwise, current time is used.
time_grain: Optional. A rollup name, as P1D. OTherwise, default rollup for the metrics is used.
More information and metrics name at:
http://msdn.microsoft.com/en-us/library/azure/dn166964.aspx
'''
metrics = ('names='+','.join(metrics)) if metrics else ''
start_time = ('StartTime='+start_time) if start_time else ''
end_time = ('EndTime='+end_time) if end_time else ''
time_grain = ('TimeGrain='+time_grain) if time_grain else ''
parameters = ('&'.join(v for v in (metrics, start_time, end_time, time_grain) if v))
parameters = '?'+parameters if parameters else ''
return self._perform_get(self._get_historical_usage_metrics_path(webspace_name, website_name) + parameters,
MetricResponses)
def get_metric_definitions(self, webspace_name, website_name):
'''
Get metric definitions of metrics available of this web site.
webspace_name: The name of the webspace.
website_name: The name of the website.
'''
return self._perform_get(self._get_metric_definitions_path(webspace_name, website_name),
MetricDefinitions)
def get_publish_profile_xml(self, webspace_name, website_name):
'''
Get a site's publish profile as a string
webspace_name: The name of the webspace.
website_name: The name of the website.
'''
return self._perform_get(self._get_publishxml_path(webspace_name, website_name),
None).body.decode("utf-8")
def get_publish_profile(self, webspace_name, website_name):
'''
Get a site's publish profile as an object
webspace_name: The name of the webspace.
website_name: The name of the website.
'''
return self._perform_get(self._get_publishxml_path(webspace_name, website_name),
PublishData)
#--Helper functions --------------------------------------------------
def _get_list_webspaces_path(self):
return self._get_path('services/webspaces', None)
def _get_webspace_details_path(self, webspace_name):
return self._get_path('services/webspaces/', webspace_name)
def _get_sites_path(self, webspace_name):
return self._get_path('services/webspaces/',
webspace_name) + '/sites'
def _get_sites_details_path(self, webspace_name, website_name):
return self._get_path('services/webspaces/',
webspace_name) + '/sites/' + _str(website_name)
def _get_restart_path(self, webspace_name, website_name):
return self._get_path('services/webspaces/',
webspace_name) + '/sites/' + _str(website_name) + '/restart/'
def _get_historical_usage_metrics_path(self, webspace_name, website_name):
return self._get_path('services/webspaces/',
webspace_name) + '/sites/' + _str(website_name) + '/metrics/'
def _get_metric_definitions_path(self, webspace_name, website_name):
return self._get_path('services/webspaces/',
webspace_name) + '/sites/' + _str(website_name) + '/metricdefinitions/'
def _get_publishxml_path(self, webspace_name, website_name):
return self._get_path('services/webspaces/',
webspace_name) + '/sites/' + _str(website_name) + '/publishxml/'

View File

@@ -12,12 +12,12 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
#-------------------------------------------------------------------------- #--------------------------------------------------------------------------
import hashlib
import hmac
import sys import sys
import types import types
from datetime import datetime from datetime import datetime
from dateutil import parser
from dateutil.tz import tzutc
from xml.dom import minidom from xml.dom import minidom
from azure import (WindowsAzureData, from azure import (WindowsAzureData,
WindowsAzureError, WindowsAzureError,
@@ -36,6 +36,7 @@ from azure import (WindowsAzureData,
_general_error_handler, _general_error_handler,
_list_of, _list_of,
_parse_response_for_dict, _parse_response_for_dict,
_sign_string,
_unicode_type, _unicode_type,
_ERROR_CANNOT_SERIALIZE_VALUE_TO_ENTITY, _ERROR_CANNOT_SERIALIZE_VALUE_TO_ENTITY,
) )
@@ -408,7 +409,7 @@ def _update_storage_header(request):
if request.body: if request.body:
assert isinstance(request.body, bytes) assert isinstance(request.body, bytes)
# if it is PUT, POST, MERGE, DELETE, need to add content-lengt to header. # if it is PUT, POST, MERGE, DELETE, need to add content-length to header.
if request.method in ['PUT', 'POST', 'MERGE', 'DELETE']: if request.method in ['PUT', 'POST', 'MERGE', 'DELETE']:
request.headers.append(('Content-Length', str(len(request.body)))) request.headers.append(('Content-Length', str(len(request.body))))
@@ -540,17 +541,6 @@ def _sign_storage_table_request(request, account_name, account_key):
return auth_string return auth_string
def _sign_string(account_key, string_to_sign):
decoded_account_key = _decode_base64_to_bytes(account_key)
if isinstance(string_to_sign, _unicode_type):
string_to_sign = string_to_sign.encode('utf-8')
signed_hmac_sha256 = hmac.HMAC(
decoded_account_key, string_to_sign, hashlib.sha256)
digest = signed_hmac_sha256.digest()
encoded_digest = _encode_base64(digest)
return encoded_digest
def _to_python_bool(value): def _to_python_bool(value):
if value.lower() == 'true': if value.lower() == 'true':
return True return True
@@ -572,7 +562,12 @@ def _to_entity_bool(value):
def _to_entity_datetime(value): def _to_entity_datetime(value):
return 'Edm.DateTime', value.strftime('%Y-%m-%dT%H:%M:%S') # Azure expects the date value passed in to be UTC.
# Azure will always return values as UTC.
# If a date is passed in without timezone info, it is assumed to be UTC.
if value.tzinfo:
value = value.astimezone(tzutc())
return 'Edm.DateTime', value.strftime('%Y-%m-%dT%H:%M:%SZ')
def _to_entity_float(value): def _to_entity_float(value):
@@ -607,12 +602,9 @@ def _from_entity_int(value):
def _from_entity_datetime(value): def _from_entity_datetime(value):
format = '%Y-%m-%dT%H:%M:%S' # Note that Azure always returns UTC datetime, and dateutil parser
if '.' in value: # will set the tzinfo on the date it returns
format = format + '.%f' return parser.parse(value)
if value.endswith('Z'):
format = format + 'Z'
return datetime.strptime(value, format)
_ENTITY_TO_PYTHON_CONVERSIONS = { _ENTITY_TO_PYTHON_CONVERSIONS = {
'Edm.Binary': _from_entity_binary, 'Edm.Binary': _from_entity_binary,
@@ -705,7 +697,7 @@ def _convert_entity_to_xml(source):
if sys.version_info < (3,): if sys.version_info < (3,):
if isinstance(properties_str, unicode): if isinstance(properties_str, unicode):
properties_str = properties_str.encode(encoding='utf-8') properties_str = properties_str.encode('utf-8')
# generate the entity_body # generate the entity_body
entity_body = entity_body.format(properties=properties_str) entity_body = entity_body.format(properties=properties_str)
@@ -835,10 +827,6 @@ def _convert_xml_to_entity(xmlstr):
# extract each property node and get the type from attribute and node value # extract each property node and get the type from attribute and node value
for xml_property in xml_properties[0].childNodes: for xml_property in xml_properties[0].childNodes:
name = _remove_prefix(xml_property.nodeName) name = _remove_prefix(xml_property.nodeName)
# exclude the Timestamp since it is auto added by azure when
# inserting entity. We don't want this to mix with real properties
if name in ['Timestamp']:
continue
if xml_property.firstChild: if xml_property.firstChild:
value = xml_property.firstChild.nodeValue value = xml_property.firstChild.nodeValue

View File

@@ -62,6 +62,7 @@ else:
# Keep this value sync with _ERROR_PAGE_BLOB_SIZE_ALIGNMENT # Keep this value sync with _ERROR_PAGE_BLOB_SIZE_ALIGNMENT
_PAGE_SIZE = 512 _PAGE_SIZE = 512
class BlobService(_StorageClient): class BlobService(_StorageClient):
''' '''
@@ -101,6 +102,7 @@ class BlobService(_StorageClient):
Live host base url. If not specified, uses the host base specified Live host base url. If not specified, uses the host base specified
when BlobService was initialized. when BlobService was initialized.
''' '''
if not account_name: if not account_name:
account_name = self.account_name account_name = self.account_name
if not protocol: if not protocol:
@@ -553,6 +555,7 @@ class BlobService(_StorageClient):
request, self.use_local_storage) request, self.use_local_storage)
request.headers = _update_storage_blob_header( request.headers = _update_storage_blob_header(
request, self.account_name, self.account_key) request, self.account_name, self.account_key)
response = self._perform_request(request) response = self._perform_request(request)
return _parse_response_for_dict(response) return _parse_response_for_dict(response)
@@ -740,26 +743,6 @@ class BlobService(_StorageClient):
_validate_not_none('container_name', container_name) _validate_not_none('container_name', container_name)
_validate_not_none('blob_name', blob_name) _validate_not_none('blob_name', blob_name)
_validate_not_none('file_path', file_path) _validate_not_none('file_path', file_path)
request = HTTPRequest()
request.method = 'PUT'
request.host = self._get_host()
request.path = '/' + _str(container_name) + '/' + _str(blob_name) + ''
request.headers = [
('x-ms-blob-type', 'BlockBlob'),
('Content-Encoding', _str_or_none(content_encoding)),
('Content-Language', _str_or_none(content_language)),
('Content-MD5', _str_or_none(content_md5)),
('Cache-Control', _str_or_none(cache_control)),
('x-ms-blob-content-type', _str_or_none(x_ms_blob_content_type)),
('x-ms-blob-content-encoding',
_str_or_none(x_ms_blob_content_encoding)),
('x-ms-blob-content-language',
_str_or_none(x_ms_blob_content_language)),
('x-ms-blob-content-md5', _str_or_none(x_ms_blob_content_md5)),
('x-ms-blob-cache-control', _str_or_none(x_ms_blob_cache_control)),
('x-ms-meta-name-values', x_ms_meta_name_values),
('x-ms-lease-id', _str_or_none(x_ms_lease_id)),
]
count = path.getsize(file_path) count = path.getsize(file_path)
with open(file_path, 'rb') as stream: with open(file_path, 'rb') as stream:
@@ -833,26 +816,6 @@ class BlobService(_StorageClient):
_validate_not_none('container_name', container_name) _validate_not_none('container_name', container_name)
_validate_not_none('blob_name', blob_name) _validate_not_none('blob_name', blob_name)
_validate_not_none('stream', stream) _validate_not_none('stream', stream)
request = HTTPRequest()
request.method = 'PUT'
request.host = self._get_host()
request.path = '/' + _str(container_name) + '/' + _str(blob_name) + ''
request.headers = [
('x-ms-blob-type', 'BlockBlob'),
('Content-Encoding', _str_or_none(content_encoding)),
('Content-Language', _str_or_none(content_language)),
('Content-MD5', _str_or_none(content_md5)),
('Cache-Control', _str_or_none(cache_control)),
('x-ms-blob-content-type', _str_or_none(x_ms_blob_content_type)),
('x-ms-blob-content-encoding',
_str_or_none(x_ms_blob_content_encoding)),
('x-ms-blob-content-language',
_str_or_none(x_ms_blob_content_language)),
('x-ms-blob-content-md5', _str_or_none(x_ms_blob_content_md5)),
('x-ms-blob-cache-control', _str_or_none(x_ms_blob_cache_control)),
('x-ms-meta-name-values', x_ms_meta_name_values),
('x-ms-lease-id', _str_or_none(x_ms_lease_id)),
]
if count and count < self._BLOB_MAX_DATA_SIZE: if count and count < self._BLOB_MAX_DATA_SIZE:
if progress_callback: if progress_callback:
@@ -922,7 +885,14 @@ class BlobService(_StorageClient):
else: else:
break break
self.put_block_list(container_name, blob_name, block_ids) self.put_block_list(container_name, blob_name, block_ids,
content_md5, x_ms_blob_cache_control,
x_ms_blob_content_type,
x_ms_blob_content_encoding,
x_ms_blob_content_language,
x_ms_blob_content_md5,
x_ms_meta_name_values,
x_ms_lease_id)
def put_block_blob_from_bytes(self, container_name, blob_name, blob, def put_block_blob_from_bytes(self, container_name, blob_name, blob,
index=0, count=None, content_encoding=None, index=0, count=None, content_encoding=None,
@@ -980,26 +950,6 @@ class BlobService(_StorageClient):
_validate_not_none('blob', blob) _validate_not_none('blob', blob)
_validate_not_none('index', index) _validate_not_none('index', index)
_validate_type_bytes('blob', blob) _validate_type_bytes('blob', blob)
request = HTTPRequest()
request.method = 'PUT'
request.host = self._get_host()
request.path = '/' + _str(container_name) + '/' + _str(blob_name) + ''
request.headers = [
('x-ms-blob-type', 'BlockBlob'),
('Content-Encoding', _str_or_none(content_encoding)),
('Content-Language', _str_or_none(content_language)),
('Content-MD5', _str_or_none(content_md5)),
('Cache-Control', _str_or_none(cache_control)),
('x-ms-blob-content-type', _str_or_none(x_ms_blob_content_type)),
('x-ms-blob-content-encoding',
_str_or_none(x_ms_blob_content_encoding)),
('x-ms-blob-content-language',
_str_or_none(x_ms_blob_content_language)),
('x-ms-blob-content-md5', _str_or_none(x_ms_blob_content_md5)),
('x-ms-blob-cache-control', _str_or_none(x_ms_blob_cache_control)),
('x-ms-meta-name-values', x_ms_meta_name_values),
('x-ms-lease-id', _str_or_none(x_ms_lease_id)),
]
if index < 0: if index < 0:
raise TypeError(_ERROR_VALUE_NEGATIVE.format('index')) raise TypeError(_ERROR_VALUE_NEGATIVE.format('index'))
@@ -1101,26 +1051,6 @@ class BlobService(_StorageClient):
_validate_not_none('container_name', container_name) _validate_not_none('container_name', container_name)
_validate_not_none('blob_name', blob_name) _validate_not_none('blob_name', blob_name)
_validate_not_none('text', text) _validate_not_none('text', text)
request = HTTPRequest()
request.method = 'PUT'
request.host = self._get_host()
request.path = '/' + _str(container_name) + '/' + _str(blob_name) + ''
request.headers = [
('x-ms-blob-type', 'BlockBlob'),
('Content-Encoding', _str_or_none(content_encoding)),
('Content-Language', _str_or_none(content_language)),
('Content-MD5', _str_or_none(content_md5)),
('Cache-Control', _str_or_none(cache_control)),
('x-ms-blob-content-type', _str_or_none(x_ms_blob_content_type)),
('x-ms-blob-content-encoding',
_str_or_none(x_ms_blob_content_encoding)),
('x-ms-blob-content-language',
_str_or_none(x_ms_blob_content_language)),
('x-ms-blob-content-md5', _str_or_none(x_ms_blob_content_md5)),
('x-ms-blob-cache-control', _str_or_none(x_ms_blob_cache_control)),
('x-ms-meta-name-values', x_ms_meta_name_values),
('x-ms-lease-id', _str_or_none(x_ms_lease_id)),
]
if not isinstance(text, bytes): if not isinstance(text, bytes):
_validate_not_none('text_encoding', text_encoding) _validate_not_none('text_encoding', text_encoding)
@@ -1541,7 +1471,7 @@ class BlobService(_StorageClient):
index = 0 index = 0
while index < blob_size: while index < blob_size:
chunk_range = 'bytes={}-{}'.format( chunk_range = 'bytes={0}-{1}'.format(
index, index,
index + self._BLOB_MAX_CHUNK_DATA_SIZE - 1) index + self._BLOB_MAX_CHUNK_DATA_SIZE - 1)
data = self.get_blob( data = self.get_blob(
@@ -1862,6 +1792,7 @@ class BlobService(_StorageClient):
('x-ms-lease-id', _str_or_none(x_ms_lease_id)), ('x-ms-lease-id', _str_or_none(x_ms_lease_id)),
('x-ms-source-lease-id', _str_or_none(x_ms_source_lease_id)) ('x-ms-source-lease-id', _str_or_none(x_ms_source_lease_id))
] ]
request.path, request.query = _update_request_uri_query_local_storage( request.path, request.query = _update_request_uri_query_local_storage(
request, self.use_local_storage) request, self.use_local_storage)
request.headers = _update_storage_blob_header( request.headers = _update_storage_blob_header(
@@ -1904,7 +1835,8 @@ class BlobService(_StorageClient):
self._perform_request(request) self._perform_request(request)
def delete_blob(self, container_name, blob_name, snapshot=None, def delete_blob(self, container_name, blob_name, snapshot=None,
x_ms_lease_id=None): timeout=None, x_ms_lease_id=None,
x_ms_delete_snapshots=None):
''' '''
Marks the specified blob or snapshot for deletion. The blob is later Marks the specified blob or snapshot for deletion. The blob is later
deleted during garbage collection. deleted during garbage collection.
@@ -1917,7 +1849,22 @@ class BlobService(_StorageClient):
snapshot: snapshot:
Optional. The snapshot parameter is an opaque DateTime value that, Optional. The snapshot parameter is an opaque DateTime value that,
when present, specifies the blob snapshot to delete. when present, specifies the blob snapshot to delete.
timeout:
Optional. The timeout parameter is expressed in seconds.
The Blob service returns an error when the timeout interval elapses
while processing the request.
x_ms_lease_id: Required if the blob has an active lease. x_ms_lease_id: Required if the blob has an active lease.
x_ms_delete_snapshots:
Required if the blob has associated snapshots. Specify one of the
following two options:
include: Delete the base blob and all of its snapshots.
only: Delete only the blob's snapshots and not the blob itself.
This header should be specified only for a request against the base
blob resource. If this header is specified on a request to delete
an individual snapshot, the Blob service returns status code 400
(Bad Request). If this header is not specified on the request and
the blob has associated snapshots, the Blob service returns status
code 409 (Conflict).
''' '''
_validate_not_none('container_name', container_name) _validate_not_none('container_name', container_name)
_validate_not_none('blob_name', blob_name) _validate_not_none('blob_name', blob_name)
@@ -1925,8 +1872,14 @@ class BlobService(_StorageClient):
request.method = 'DELETE' request.method = 'DELETE'
request.host = self._get_host() request.host = self._get_host()
request.path = '/' + _str(container_name) + '/' + _str(blob_name) + '' request.path = '/' + _str(container_name) + '/' + _str(blob_name) + ''
request.headers = [('x-ms-lease-id', _str_or_none(x_ms_lease_id))] request.headers = [
request.query = [('snapshot', _str_or_none(snapshot))] ('x-ms-lease-id', _str_or_none(x_ms_lease_id)),
('x-ms-delete-snapshots', _str_or_none(x_ms_delete_snapshots))
]
request.query = [
('snapshot', _str_or_none(snapshot)),
('timeout', _int_or_none(timeout))
]
request.path, request.query = _update_request_uri_query_local_storage( request.path, request.query = _update_request_uri_query_local_storage(
request, self.use_local_storage) request, self.use_local_storage)
request.headers = _update_storage_blob_header( request.headers = _update_storage_blob_header(

View File

@@ -12,11 +12,12 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
#-------------------------------------------------------------------------- #--------------------------------------------------------------------------
from azure import url_quote from azure import _sign_string, url_quote
from azure.storage import _sign_string, X_MS_VERSION from azure.storage import X_MS_VERSION
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
# Constants for the share access signature # Constants for the share access signature
SIGNED_VERSION = 'sv'
SIGNED_START = 'st' SIGNED_START = 'st'
SIGNED_EXPIRY = 'se' SIGNED_EXPIRY = 'se'
SIGNED_RESOURCE = 'sr' SIGNED_RESOURCE = 'sr'