Merge branch 'master' into pip-requirements

Moved the pylint requirement to the requirements.txt file

# Conflicts:
#	.travis.yml
This commit is contained in:
Derek Bekoe 2016-03-02 10:23:33 -08:00
Родитель 6fd173d0c4 05803c4757
Коммит 2fc6d50d1c
28 изменённых файлов: 818 добавлений и 143 удалений

Просмотреть файл

@ -7,4 +7,6 @@ install:
- pip install -r requirements.txt
script:
- export PYTHONPATH=$PATHONPATH:./src
- python -m unittest discover -s src/azure/cli/tests
- python -m azure.cli
- pylint src/azure
- python -m unittest discover -s src/azure/cli/tests

36
Dockerfile Normal file
Просмотреть файл

@ -0,0 +1,36 @@
FROM ubuntu:14.04
RUN rm /bin/sh && ln -s /bin/bash /bin/sh
RUN apt-get update -qq && \
apt-get install -qqy --no-install-recommends\
build-essential \
curl \
ca-certificates \
git \
python-pip \
libffi-dev \
libssl-dev \
python-dev \
vim \
nano \
jq && \
rm -rf /var/lib/apt/lists/* && \
pip install azure==2.0.0a1 && \
pip install --upgrade requests && \
pip install cryptography && \
pip install pyopenssl ndg-httpsclient pyasn1
ENV AZURECLITEMP /tmp/azure-cli
ENV PYTHONPATH $PYTHONPATH:$AZURECLITEMP/src
ENV PATH $PATH:$AZURECLITEMP
RUN mkdir -p $AZURECLITEMP
ADD src $AZURECLITEMP/src
RUN echo '#!/bin/bash'>$AZURECLITEMP/az && \
echo 'python -m azure.cli "$@"'>>$AZURECLITEMP/az && \
chmod +x $AZURECLITEMP/az && \
az
ENV EDITOR vim

15
az Executable file
Просмотреть файл

@ -0,0 +1,15 @@
#!/bin/bash
SOURCE="${BASH_SOURCE[0]}"
while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink
DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
SOURCE="$(readlink "$SOURCE")"
[[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located
done
DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
export PYTHONPATH="${DIR}/src:${PYTHONPATH}"
python -m azure.cli "$@"

7
az.bat Normal file
Просмотреть файл

@ -0,0 +1,7 @@
@echo off
setlocal
SET PYTHONPATH=%~dp0/src;%PYTHONPATH%
python -m azure.cli %*
endlocal

Просмотреть файл

@ -14,6 +14,9 @@
<LaunchProvider>Standard Python launcher</LaunchProvider>
<InterpreterId>{1dd9c42b-5980-42ce-a2c3-46d3bf0eede4}</InterpreterId>
<InterpreterVersion>3.5</InterpreterVersion>
<CommandLineArguments>
</CommandLineArguments>
<EnableNativeCodeDebugging>False</EnableNativeCodeDebugging>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Debug'" />
<PropertyGroup Condition="'$(Configuration)' == 'Release'" />
@ -25,15 +28,23 @@
<Compile Include="azure\cli\commands\account.py" />
<Compile Include="azure\cli\commands\login.py" />
<Compile Include="azure\cli\commands\logout.py" />
<Compile Include="azure\cli\commands\network.py" />
<Compile Include="azure\cli\commands\resourcegroup.py" />
<Compile Include="azure\cli\commands\storage.py" />
<Compile Include="azure\cli\commands\vm.py" />
<Compile Include="azure\cli\commands\_auto_command.py" />
<Compile Include="azure\cli\commands\__init__.py" />
<Compile Include="azure\cli\main.py" />
<Compile Include="azure\cli\tests\test_argparse.py">
<SubType>Code</SubType>
</Compile>
<Compile Include="azure\cli\tests\test_autocommand.py">
<SubType>Code</SubType>
</Compile>
<Compile Include="azure\cli\tests\test_connection_verify.py">
<SubType>Code</SubType>
</Compile>
<Compile Include="azure\cli\tests\test_output.py" />
<Compile Include="azure\cli\tests\test_profile.py" />
<Compile Include="azure\cli\_argparse.py" />
<Compile Include="azure\cli\commands\_command_creation.py">
@ -42,7 +53,9 @@
<Compile Include="azure\cli\_debug.py">
<SubType>Code</SubType>
</Compile>
<Compile Include="azure\cli\_locale.py" />
<Compile Include="azure\cli\_logging.py" />
<Compile Include="azure\cli\_output.py" />
<Compile Include="azure\cli\_profile.py" />
<Compile Include="azure\cli\_session.py">
<SubType>Code</SubType>

Просмотреть файл

@ -0,0 +1,72 @@
Setting up your development environment
========================================
The Azure Python CLI projects sources are located on GitHub (https://github.com/Azure/azure-cli/). In order to contribute to the project, you are expected to:
- Have a GitHub account. For Microsoft contributors, follow the guidelines on https://opensourcehub.microsoft.com/ to create, configure and link your account
- Fork the https://github.com/Azure/azure-cli/ repository into your private GitHub account
- Create pull requests against the httips://github.com/azure/azure-cli repository to get your code changes merged into the project repository.
##Preparing your machine
+ Install Python 3.5.x from http://python.org. Please note that the version of Python that comes preinstalled on OSX is 2.7.
+ Clone your repository and check out the master branch
+ Create a new virtual environment “env” for Python 3.5 in the root of your clone. You can do this by running:
Windows
```BatchFile
python.exe -m venv <clone root>\env
```
OSX/Ubuntu
```Shell
python –m venv <clone root>/env
```
+ Activate the env virtual environment by running:
Windows:
```BatchFile
<clone root>\env\scripts\activate.bat
```
OSX/Ubuntu (bash):
```Shell
. <clone root>/env/bin/activate
```
+ Install the latest autorest generated azure sdk.
```Shell
python –m pip install azure==2.0.0a1
```
+ Add <clone root>\src to your PYTHONPATH environment variable:
Windows:
```BatchFile
set PYTHONPATH=<clone root>\src;%PYTHONPATH%
```
OSX/Ubuntu (bash):
```Shell
export PYTHONPATH=<clone root>/src:${PYTHONPATH}
```
##Configuring your IDE
###Visual Studio (Windows only)
+ Install Python Tools for Visual Studio. As of 2/18/2016, the current version (PTVS 2.2) can be found here.
+ Open the azure-cli.pyproj project
You should now be able to launch your project by pressing F5/start debugging
###Visual Studio Code (Any platform)
Experimental steps – still havent been able to get virtual environments to work well with VSCode
+ Install VS Code
+ Install (one of) the python extension(s) (https://marketplace.visualstudio.com/items?itemName=donjayamanne.python)
Debugging should now work (including stepping and setting breakpoints).
The repo has a launch.json file that will launch the version of Python that is first on your path.
##Running unit tests:
###Command line:
If you have configured your PYTHONPATH correctly (see above), you should be able to run all unit tests by executing python -m unittest from your <clone root>/src directory.
###VS Code:
<Working on it>
###Visual Studio
<Working on it>

12
pylintrc Normal file
Просмотреть файл

@ -0,0 +1,12 @@
[MESSAGES CONTROL]
# For all codes, run 'pylint --list-msgs' or go to 'http://pylint-messages.wikidot.com/all-codes'
# C0111 Missing docstring
# C0103 Invalid %s name "%s"
# I0011 Warning locally suppressed using disable-msg
# W0511 fixme
disable=C0111,C0103,I0011,W0511
[DESIGN]
# Maximum number of locals for function / method body
max-locals=25
# Maximum number of branch for function / method body
max-branches=20

Просмотреть файл

@ -1,3 +1,3 @@
azure==2.0.0a1
mock==1.3.0
pylint==1.5.4

Просмотреть файл

@ -1,9 +1,7 @@
from __future__ import print_function
import json
import os
from __future__ import print_function
import sys
from ._locale import get_file as locale_get_file
from ._locale import L, get_file as locale_get_file
from ._logging import logger
# Named arguments are prefixed with one of these strings
@ -20,7 +18,7 @@ class IncorrectUsageError(Exception):
pass
class Arguments(dict):
def __init__(self, source=None):
def __init__(self, source=None): #pylint: disable=super-init-not-called
self.positional = []
if source:
self.update(source)
@ -40,7 +38,7 @@ class Arguments(dict):
except LookupError:
pass
logger.debug('Argument %s is required', key)
raise IncorrectUsageError(_("Argument {0} is required").format(key))
raise IncorrectUsageError(L('Argument {0} is required').format(key))
def _read_arg(string):
for prefix in ARG_PREFIXES:
@ -66,11 +64,15 @@ class ArgumentParser(object):
self.noun_map = {
'$doc': 'azure-cli.txt',
}
self.help_args = { '--help', '-h' }
self.complete_args = { '--complete' }
self.global_args = { '--verbose', '--debug' }
self.help_args = {'--help', '-h'}
self.complete_args = {'--complete'}
self.global_args = {'--verbose', '--debug'}
def add_command(self, handler, name=None, description=None, args=None):
def add_command(self,
handler,
name=None,
description=None,
args=None):
'''Registers a command that may be parsed by this parser.
`handler` is the function to call with two `Arguments` objects.
@ -105,7 +107,7 @@ class ArgumentParser(object):
m['$args'] = []
m['$kwargs'] = kw = {}
m['$argdoc'] = ad = []
for spec, desc in (args or []):
for spec, desc in args or []:
if not any(spec.startswith(p) for p in ARG_PREFIXES):
m['$args'].append(spec.strip('<> '))
ad.append((spec, desc))
@ -121,7 +123,11 @@ class ArgumentParser(object):
ad.append(('/'.join(aliases), desc))
def execute(self, args, show_usage=False, show_completions=False, out=sys.stdout):
def execute(self,
args,
show_usage=False,
show_completions=False,
out=sys.stdout):
'''Parses `args` and invokes the associated handler.
The handler is passed two `Arguments` objects with all arguments other
@ -142,10 +148,11 @@ class ArgumentParser(object):
if not show_completions:
show_completions = any(a in self.complete_args for a in args)
all_global_args = set(a.lstrip('-/') for a in self.help_args | self.complete_args | self.global_args)
all_global_args = set(
a.lstrip('-/') for a in self.help_args | self.complete_args | self.global_args)
def not_global(a):
return a.lstrip('-/') not in all_global_args
it = filter(not_global, args).__iter__()
it = filter(not_global, args).__iter__() #pylint: disable=bad-builtin
m = self.noun_map
nouns = []
@ -161,17 +168,16 @@ class ArgumentParser(object):
n = next(it, '')
try:
expected_args = m['$args']
expected_kwargs = m['$kwargs']
handler = m['$handler']
except LookupError:
logger.debug('Missing data for noun %s', n)
show_usage = True
if show_completions:
return self._display_completions(nouns, m, args, out)
return ArgumentParser._display_completions(m, out)
if show_usage:
return self._display_usage(nouns, m, args, out)
return self._display_usage(nouns, m, out)
parsed = Arguments()
others = Arguments()
@ -189,8 +195,9 @@ class ArgumentParser(object):
elif target_value[1] is True:
# Arg with no value
if value is not None:
print(_("argument '{0}' does not take a value").format(key_n), file=out)
return self._display_usage(nouns, m, args, out)
print(L("argument '{0}' does not take a value").format(key_n),
file=out)
return self._display_usage(nouns, m, out)
parsed.add_from_dotted(target_value[0], True)
else:
# Arg with a value
@ -208,16 +215,16 @@ class ArgumentParser(object):
return handler(parsed, others)
except IncorrectUsageError as ex:
print(str(ex), file=out)
return self.display_usage(nouns, m, args, out)
return self._display_usage(nouns, m, out)
finally:
sys.stdout = old_stdout
def _display_usage(self, nouns, noun_map, arguments, out=sys.stdout):
def _display_usage(self, nouns, noun_map, out=sys.stdout):
spec = ' '.join(noun_map.get('$spec') or nouns)
print(' {} {}'.format(self.prog, spec), file=out)
print(file=out)
out.flush()
subnouns = sorted(k for k in noun_map if not k.startswith('$'))
if subnouns:
print('Subcommands', file=out)
@ -225,7 +232,7 @@ class ArgumentParser(object):
print(' {}'.format(n), file=out)
print(file=out)
out.flush()
argdoc = noun_map.get('$argdoc')
if argdoc:
print('Arguments', file=out)
@ -246,7 +253,8 @@ class ArgumentParser(object):
out.flush()
logger.debug('Expected documentation at %s', doc_file)
def _display_completions(self, nouns, noun_map, arguments, out=sys.stdout):
@staticmethod
def _display_completions(noun_map, out=sys.stdout):
completions = [k for k in noun_map if not k.startswith('$')]
kwargs = noun_map.get('$kwargs')

Просмотреть файл

@ -1,12 +1,14 @@
import os
import os
from ._logging import logger
DISABLE_VERIFY_VARIABLE_NAME = "AZURE_CLI_DISABLE_CONNECTION_VERIFICATION"
def allow_debug_connection(client):
if should_disable_connection_verify():
logger.warn("Connection verification disabled by environment variable %s", DISABLE_VERIFY_VARIABLE_NAME)
def allow_debug_connection(client):
if should_disable_connection_verify():
logger.warning("Connection verification disabled by environment variable %s",
DISABLE_VERIFY_VARIABLE_NAME)
client.config.connection.verify = False
def should_disable_connection_verify():
return bool(os.environ.get(DISABLE_VERIFY_VARIABLE_NAME))
def should_disable_connection_verify():
return bool(os.environ.get(DISABLE_VERIFY_VARIABLE_NAME))

Просмотреть файл

@ -1,11 +1,16 @@
import os.path
import os.path
from codecs import open as codecs_open
from codecs import open
_translations = dict()
_locale_dir = ''
def L(key):
return _translations.get(key) or '<NO_TRANSLATION:{}>'.format(key)
def install(locale_dir):
mapping = []
with open(os.path.join(locale_dir, "messages.txt"), 'r', encoding='utf-8-sig') as f:
with codecs_open(os.path.join(locale_dir, "messages.txt"), 'r', encoding='utf-8-sig') as f:
for i in f:
if not i or i.startswith('#') or not i.strip():
continue
@ -13,18 +18,14 @@ def install(locale_dir):
mapping.append((i[5:].strip(), None))
else:
mapping[-1] = (mapping[-1][0], i.strip())
translations = dict(mapping)
def _(key):
return translations.get(key) or '<NO_TRANSLATION:{}>'.format(key)
_.locale_dir = locale_dir
__builtins__['_'] = _
globals()['_translations'] = dict(mapping)
globals()['_locale_dir'] = locale_dir
def get_file(name):
try:
src = _.locale_dir
src = _locale_dir
except (NameError, AttributeError):
raise RuntimeError("localizations not installed")
return os.path.join(src, name)

Просмотреть файл

@ -1,7 +1,7 @@
import logging as _logging
import logging as _logging
import sys
__all__ = ['logging', 'configure_logging']
__all__ = ['logger', 'configure_logging']
logger = _logging.Logger('az', _logging.WARNING)
@ -13,7 +13,7 @@ def _arg_name(arg):
def configure_logging(argv, config):
level = _logging.WARNING
# Load logging info from config
if config.get('verbose'):
level = _logging.INFO

Просмотреть файл

@ -1,4 +1,4 @@
from __future__ import print_function, unicode_literals
from __future__ import print_function, unicode_literals
import sys
import json
@ -8,7 +8,7 @@ try:
from io import StringIO
except ImportError:
# Python 2
from StringIO import StringIO
from StringIO import StringIO #pylint: disable=import-error
class OutputFormatException(Exception):
pass
@ -40,9 +40,9 @@ def format_text(obj):
except TypeError:
return ''
class OutputProducer(object):
def __init__(self, formatter=format_json, file=sys.stdout):
class OutputProducer(object): #pylint: disable=too-few-public-methods
def __init__(self, formatter=format_json, file=sys.stdout): #pylint: disable=redefined-builtin
self.formatter = formatter
self.file = file
@ -58,7 +58,7 @@ class TableOutput(object):
def dump(self):
if len(self._rows) == 1:
return
with StringIO() as io:
cols = [(c, self._columns[c]) for c in self._column_order]
io.write(' | '.join(c.center(w) for c, w in cols))
@ -91,7 +91,7 @@ class TextOutput(object):
def __init__(self):
self.identifiers = {}
def add(self, identifier, value):
if identifier in self.identifiers:
self.identifiers[identifier].append(value)
@ -100,14 +100,14 @@ class TextOutput(object):
def dump(self):
with StringIO() as io:
for id in sorted(self.identifiers):
io.write(id.upper())
for identifier in sorted(self.identifiers):
io.write(identifier.upper())
io.write('\t')
for col in self.identifiers[id]:
for col in self.identifiers[identifier]:
if isinstance(col, str):
io.write(col)
else:
# TODO: Handle complex objects
# TODO: Need to handle complex objects
io.write("null")
io.write('\t')
io.write('\n')

Просмотреть файл

@ -1,6 +1,6 @@
from msrest.authentication import BasicTokenAuthentication
import collections
from msrest.authentication import BasicTokenAuthentication
from .main import CONFIG
import collections
class Profile(object):
@ -37,10 +37,9 @@ class Profile(object):
for s in subscriptions:
s['active'] = False
if new_active_one:
new_active_one['active'] = True
else:
new_subscriptions[0]['active'] = True
if not new_active_one:
new_active_one = new_subscriptions[0]
new_active_one['active'] = True
else:
new_subscriptions[0]['active'] = True
@ -60,20 +59,20 @@ class Profile(object):
raise ValueError('Please run "account set" to select active account.')
return BasicTokenAuthentication(
{'access_token': active[0]['access_token']}), active[0]['id']
{'access_token': active[0]['access_token']}), active[0]['id']
def set_active_subscription(self, subscription_id_or_name):
subscriptions = self.load_subscriptions()
subscription_id_or_name = subscription_id_or_name.lower()
result = [x for x in subscriptions
if subscription_id_or_name == x['id'].lower() or
result = [x for x in subscriptions
if subscription_id_or_name == x['id'].lower() or
subscription_id_or_name == x['name'].lower()]
if len(result) != 1:
raise ValueError('The subscription of "{}" does not exist or has more than'
' one match.'.format(subscription_id_or_name))
for s in subscriptions:
s['active'] = False
result[0]['active'] = True
@ -91,7 +90,7 @@ class Profile(object):
subscriptions[0]['active'] = True
self._save_subscriptions(subscriptions)
def load_subscriptions(self):
return self._storage.get('subscriptions') or []

Просмотреть файл

@ -7,7 +7,7 @@ except ImportError:
import collections
from codecs import open
from codecs import open as codecs_open
class Session(collections.MutableMapping):
'''A simple dict-like class that is backed by a JSON file.
@ -28,14 +28,14 @@ class Session(collections.MutableMapping):
st = os.stat(self.filename)
if st.st_mtime + max_age < time.clock():
self.save()
with open(self.filename, 'r', encoding='utf-8-sig') as f:
with codecs_open(self.filename, 'r', encoding='utf-8-sig') as f:
self.data = json.load(f)
except (OSError, IOError):
self.save()
def save(self):
if self.filename:
with open(self.filename, 'w', encoding='utf-8-sig') as f:
with codecs_open(self.filename, 'w', encoding='utf-8-sig') as f:
json.dump(self.data, f)
def save_with_retry(self, retries=5):

Просмотреть файл

@ -1,3 +1,2 @@
def normalize_newlines(str_to_normalize):
return str_to_normalize.replace('\r\n', '\n')

Просмотреть файл

@ -1,13 +1,15 @@
from .._argparse import IncorrectUsageError
from .._argparse import IncorrectUsageError
from .._logging import logger
# TODO: Alternatively, simply scan the directory for all modules
COMMAND_MODULES = [
'account',
'login',
'logout',
'account',
'network',
'resourcegroup',
'storage',
'resourcegroup'
'vm',
]
_COMMANDS = {}
@ -19,33 +21,33 @@ def command(name):
return handler
return add_command
def description(description):
def description(description_text):
def add_description(handler):
_COMMANDS.setdefault(handler, {})['description'] = description
logger.debug('Added description "%s" to %s', description, handler)
_COMMANDS.setdefault(handler, {})['description'] = description_text
logger.debug('Added description "%s" to %s', description_text, handler)
return handler
return add_description
def option(spec, description=None):
def option(spec, description_text=None):
def add_option(handler):
_COMMANDS.setdefault(handler, {}).setdefault('args', []).append((spec, description))
_COMMANDS.setdefault(handler, {}).setdefault('args', []).append((spec, description_text))
logger.debug('Added option "%s" to %s', spec, handler)
return handler
return add_option
def add_to_parser(parser, command=None):
def add_to_parser(parser, command_name=None):
'''Loads commands into the parser
When `command` is specified, only commands from that module will be loaded.
If the module is not found, all commands are loaded.
'''
# Importing the modules is sufficient to invoke the decorators. Then we can
# get all of the commands from the _COMMANDS variable.
loaded = False
if command:
if command_name:
try:
__import__('azure.cli.commands.' + command)
__import__('azure.cli.commands.' + command_name)
loaded = True
except ImportError:
# Unknown command - we'll load all below

Просмотреть файл

@ -0,0 +1,58 @@
import inspect
from msrest import Serializer
from ..commands import command, description, option
from azure.cli._argparse import IncorrectUsageError
def _decorate_command(name, func):
return command(name)(func)
def _decorate_description(desc, func):
return description(desc)(func)
def _decorate_option(spec, descr, func):
return option(spec, descr)(func)
def _make_func(client_factory, member_name, return_type_name, unbound_func):
def call_client(args, unexpected): #pylint: disable=unused-argument
client = client_factory()
ops_instance = getattr(client, member_name)
try:
result = unbound_func(ops_instance, **args)
if not return_type_name:
return {}
return Serializer().serialize_data(result, return_type_name)
except TypeError as exception:
# TODO: Evaluate required/missing parameters and provide specific
# usage for missing params...
raise IncorrectUsageError(exception)
return call_client
def _option_description(operation, arg):
"""Pull out parameter help from doccomments of the command
"""
# TODO: We are currently doing this for every option/argument.
# We should do it (at most) once for a given command...
return ' '.join(l.split(':')[-1] for l in inspect.getdoc(operation).splitlines()
if l.startswith(':param') and arg + ':' in l)
EXCLUDED_PARAMS = frozenset(['self', 'raw', 'custom_headers', 'operation_config'])
def operation_builder(package_name, resource_type, member_name, client_type, operations):
for operation, return_type_name in operations:
opname = operation.__name__
func = _make_func(client_type, member_name, return_type_name, operation)
func = _decorate_command(' '.join([package_name, resource_type, opname]), func)
args = []
try:
# only supported in python3 - falling back to argspec if not available
sig = inspect.signature(operation)
args = sig.parameters
except AttributeError:
sig = inspect.getargspec(operation) #pylint: disable=deprecated-method
args = sig.args
for arg in [a for a in args if not a in EXCLUDED_PARAMS]:
spec = '--%s <%s>' % (arg, arg)
func = _decorate_option(spec, _option_description(operation, arg), func=func)

Просмотреть файл

@ -1,10 +1,10 @@
from .._profile import Profile
from .._profile import Profile
import azure.cli._debug as _debug
import azure.cli as cli
def get_service_client(client_type, config_type):
profile = Profile()
client = client_type(config_type(*profile.get_login_credentials()))
_debug.allow_debug_connection(client)
_debug.allow_debug_connection(client)
client.config.add_user_agent("AZURECLI_{}".format(cli.__version__))
return client

Просмотреть файл

@ -1,21 +1,22 @@
from .._profile import Profile
from ..commands import command, description, option
from .._locale import L
@command('account list')
@description(_('List the imported subscriptions.'))
def list_subscriptions(args, unexpected):
@description(L('List the imported subscriptions.'))
def list_subscriptions(args, unexpected): #pylint: disable=unused-argument
profile = Profile()
subscriptions = profile.load_subscriptions()
return subscriptions
@command('account set')
@description(_('Set the current subscription'))
@option('--subscription-id -n <subscription-id>', _('Subscription Id, unique name also works.'))
def set_active_subscription(args, unexpected):
id = args.get('subscription-id')
@description(L('Set the current subscription'))
@option('--subscription-id -n <subscription-id>', L('Subscription Id, unique name also works.'))
def set_active_subscription(args, unexpected): #pylint: disable=unused-argument
subscription_id = args.get('subscription-id')
if not id:
raise ValueError(_('Please provide subscription id or unique name.'))
raise ValueError(L('Please provide subscription id or unique name.'))
profile = Profile()
profile.set_active_subscription(id)
profile.set_active_subscription(subscription_id)

Просмотреть файл

@ -1,37 +1,40 @@
from msrestazure.azure_active_directory import UserPassCredentials
from msrest import Serializer
from msrestazure.azure_active_directory import UserPassCredentials
from azure.mgmt.resource.subscriptions import SubscriptionClient, \
SubscriptionClientConfiguration
from msrest import Serializer
from .._profile import Profile
from ..commands import command, description, option
from .._debug import should_disable_connection_verify
from .._locale import L
CLIENT_ID = '04b07795-8ddb-461a-bbee-02f9e1bf7b46'
@command('login')
@description(_('log in to an Azure subscription using Active Directory Organization Id'))
@option('--username -u <username>', _('organization Id. Microsoft Account is not yet supported.'))
@option('--password -p <password>', _('user password, will prompt if not given.'))
def login(args, unexpected):
@description(L('log in to an Azure subscription using Active Directory Organization Id'))
@option('--username -u <username>', L('organization Id. Microsoft Account is not yet supported.'))
@option('--password -p <password>', L('user password, will prompt if not given.'))
def login(args, unexpected): #pylint: disable=unused-argument
username = args.get('username')
password = args.get('password')
if not password:
import getpass
password = getpass.getpass(_('Password: '))
password = getpass.getpass(L('Password: '))
credentials = UserPassCredentials(username, password, client_id=CLIENT_ID, verify=not should_disable_connection_verify())
credentials = UserPassCredentials(username,
password,
client_id=CLIENT_ID,
verify=not should_disable_connection_verify())
client = SubscriptionClient(SubscriptionClientConfiguration(credentials))
subscriptions = client.subscriptions.list()
if not subscriptions:
raise RuntimeError(_('No subscriptions found for this account.'))
raise RuntimeError(L('No subscriptions found for this account.'))
serializable = Serializer().serialize_data(subscriptions, "[Subscription]")
#keep useful properties and not json serializable
#keep useful properties and not json serializable
profile = Profile()
consolidated = Profile.normalize_properties(username, subscriptions)
profile.set_subscriptions(consolidated, credentials.token['access_token'])

Просмотреть файл

@ -1,13 +1,14 @@
from .._profile import Profile
from ..commands import command, description, option
from .._locale import L
@command('logout')
@description(_('Log out from Azure subscription using Active Directory.'))
@option('--username -u <username>', _('User name used to log out from Azure Active Directory.'))
def logout(args, unexpected):
@description(L('Log out from Azure subscription using Active Directory.'))
@option('--username -u <username>', L('User name used to log out from Azure Active Directory.'))
def logout(args, unexpected): #pylint: disable=unused-argument
username = args.get('username')
if not username:
raise ValueError(_('Please provide a valid username to logout.'))
raise ValueError(L('Please provide a valid username to logout.'))
profile = Profile()
profile.logout(username)

Просмотреть файл

@ -0,0 +1,241 @@
from azure.mgmt.network import NetworkManagementClient, NetworkManagementClientConfiguration
from azure.mgmt.network.operations import (ApplicationGatewaysOperations,
ExpressRouteCircuitAuthorizationsOperations,
ExpressRouteCircuitPeeringsOperations,
ExpressRouteCircuitsOperations,
ExpressRouteServiceProvidersOperations,
LoadBalancersOperations,
LocalNetworkGatewaysOperations,
NetworkInterfacesOperations,
NetworkSecurityGroupsOperations,
PublicIPAddressesOperations,
RouteTablesOperations,
RoutesOperations,
SecurityRulesOperations,
SubnetsOperations,
UsagesOperations,
VirtualNetworkGatewayConnectionsOperations,
VirtualNetworkGatewaysOperations,
VirtualNetworksOperations)
from ._command_creation import get_service_client
from ..commands import _auto_command
def _network_client_factory():
return get_service_client(NetworkManagementClient, NetworkManagementClientConfiguration)
# pylint: disable=line-too-long
# Application gateways
_auto_command.operation_builder("network",
"appgateway",
"application_gateways",
_network_client_factory,
[
(ApplicationGatewaysOperations.delete, None),
(ApplicationGatewaysOperations.get, 'ApplicationGateway'),
(ApplicationGatewaysOperations.list, '[ApplicationGateway]'),
(ApplicationGatewaysOperations.list_all, '[ApplicationGateway]'),
(ApplicationGatewaysOperations.start, None),
(ApplicationGatewaysOperations.stop, None),
])
# ExpressRouteCircuitAuthorizationsOperations
_auto_command.operation_builder("network",
"expressroutecircuitauth",
"express_route_circuit_authorizations",
_network_client_factory,
[
(ExpressRouteCircuitAuthorizationsOperations.delete, None),
(ExpressRouteCircuitAuthorizationsOperations.get, 'ExpressRouteCircuitAuthorization'),
(ExpressRouteCircuitAuthorizationsOperations.list, '[ExpressRouteCircuitAuthorization]'),
])
# ExpressRouteCircuitPeeringsOperations
_auto_command.operation_builder("network",
"expressroutecircuitpeering",
"express_route_circuit_peerings",
_network_client_factory,
[
(ExpressRouteCircuitPeeringsOperations.delete, None),
(ExpressRouteCircuitPeeringsOperations.get, 'ExpressRouteCircuitPeering'),
(ExpressRouteCircuitPeeringsOperations.list, '[ExpressRouteCircuitPeering]'),
])
# ExpressRouteCircuitsOperations
_auto_command.operation_builder("network",
"expressroutecircuit",
"express_route_circuits",
_network_client_factory,
[
(ExpressRouteCircuitsOperations.delete, None),
(ExpressRouteCircuitsOperations.get, 'ExpressRouteCircuit'),
(ExpressRouteCircuitsOperations.list_arp_table, '[ExpressRouteCircuitArpTable]'),
(ExpressRouteCircuitsOperations.list_routes_table, '[ExpressRouteCircuitRoutesTable]'),
(ExpressRouteCircuitsOperations.list_stats, '[ExpressRouteCircuitStats]'),
(ExpressRouteCircuitsOperations.list, '[ExpressRouteCircuit]'),
(ExpressRouteCircuitsOperations.list_all, '[ExpressRouteCircuit]'),
])
# ExpressRouteServiceProvidersOperations
_auto_command.operation_builder("network",
"expressroutesp",
"express_route_service_providers",
_network_client_factory,
[
(ExpressRouteServiceProvidersOperations.list, '[ExpressRouteServiceProvider]'),
])
# LoadBalancersOperations
_auto_command.operation_builder("network",
"lb",
"load_balancers",
_network_client_factory,
[
(LoadBalancersOperations.delete, None),
(LoadBalancersOperations.get, 'LoadBalancer'),
(LoadBalancersOperations.list_all, '[LoadBalancer]'),
(LoadBalancersOperations.list, '[LoadBalancer]'),
])
# LocalNetworkGatewaysOperations
_auto_command.operation_builder("network",
"localgateways",
"local_network_gateways",
_network_client_factory,
[
(LocalNetworkGatewaysOperations.get, 'LocalNetworkGateway'),
(LocalNetworkGatewaysOperations.delete, None),
(LocalNetworkGatewaysOperations.list, '[LocalNetworkGateway]'),
])
# NetworkInterfacesOperations
_auto_command.operation_builder("network",
"nic",
"network_interfaces",
_network_client_factory,
[
(NetworkInterfacesOperations.delete, None),
(NetworkInterfacesOperations.get, 'NetworkInterface'),
(NetworkInterfacesOperations.list_virtual_machine_scale_set_vm_network_interfaces, '[NetworkInterface]'),
(NetworkInterfacesOperations.list_virtual_machine_scale_set_network_interfaces, '[NetworkInterface]'),
(NetworkInterfacesOperations.get_virtual_machine_scale_set_network_interface, 'NetworkInterface'),
(NetworkInterfacesOperations.list_all, '[NetworkInterface]'),
(NetworkInterfacesOperations.list, '[NetworkInterface]'),
])
# NetworkSecurityGroupsOperations
_auto_command.operation_builder("network",
"securitygroup",
"network_security_groups",
_network_client_factory,
[
(NetworkSecurityGroupsOperations.delete, None),
(NetworkSecurityGroupsOperations.delete, 'NetworkSecurityGroup'),
(NetworkSecurityGroupsOperations.list_all, '[NetworkSecurityGroup]'),
(NetworkSecurityGroupsOperations.list, '[NetworkSecurityGroup]'),
])
# PublicIPAddressesOperations
_auto_command.operation_builder("network",
"publicipaddress",
"public_ip_addresses",
_network_client_factory,
[
(PublicIPAddressesOperations.delete, None),
(PublicIPAddressesOperations.get, 'PublicIPAddress'),
(PublicIPAddressesOperations.list_all, '[PublicIPAddress]'),
(PublicIPAddressesOperations.list, '[PublicIPAddress]'),
])
# RouteTablesOperations
_auto_command.operation_builder("network",
"routetable",
"route_tables",
_network_client_factory,
[
(RouteTablesOperations.delete, None),
(RouteTablesOperations.get, 'RouteTable'),
(RouteTablesOperations.list, '[RouteTable]'),
(RouteTablesOperations.list_all, '[RouteTable]'),
])
# RoutesOperations
_auto_command.operation_builder("network",
"routeoperation",
"routes",
_network_client_factory,
[
(RoutesOperations.delete, None),
(RoutesOperations.get, 'Route'),
(RoutesOperations.list, '[Route]'),
])
# SecurityRulesOperations
_auto_command.operation_builder("network",
"securityrules",
"security_rules",
_network_client_factory,
[
(SecurityRulesOperations.delete, None),
(SecurityRulesOperations.get, 'SecurityRule'),
(SecurityRulesOperations.list, '[SecurityRule]'),
])
# SubnetsOperations
_auto_command.operation_builder("network",
"subnet",
"subnets",
_network_client_factory,
[
(SubnetsOperations.delete, None),
(SubnetsOperations.get, 'Subnet'),
(SubnetsOperations.list, '[Subnet]'),
])
# UsagesOperations
_auto_command.operation_builder("network",
"usage",
"usages",
_network_client_factory,
[
(UsagesOperations.list, '[Usage]'),
])
# VirtualNetworkGatewayConnectionsOperations
_auto_command.operation_builder("network",
"vnetgatewayconnection",
"virtual_network_gateway_connections",
_network_client_factory,
[
(VirtualNetworkGatewayConnectionsOperations.delete, None),
(VirtualNetworkGatewayConnectionsOperations.get, 'VirtualNetworkGatewayConnection'),
(VirtualNetworkGatewayConnectionsOperations.get_shared_key, 'ConnectionSharedKeyResult'),
(VirtualNetworkGatewayConnectionsOperations.list, '[VirtualNetworkGatewayConnection]'),
(VirtualNetworkGatewayConnectionsOperations.reset_shared_key, 'ConnectionResetSharedKey'),
(VirtualNetworkGatewayConnectionsOperations.set_shared_key, 'ConnectionSharedKey'),
])
# VirtualNetworkGatewaysOperations
_auto_command.operation_builder("network",
"vnetgateway",
"virtual_network_gateways",
_network_client_factory,
[
(VirtualNetworkGatewaysOperations.delete, None),
(VirtualNetworkGatewaysOperations.get, 'VirtualNetworkGateway'),
(VirtualNetworkGatewaysOperations.list, '[VirtualNetworkGateway]'),
(VirtualNetworkGatewaysOperations.reset, 'VirtualNetworkGateway'),
])
# VirtualNetworksOperations
_auto_command.operation_builder("network",
"vnet",
"virtual_networks",
_network_client_factory,
[
(VirtualNetworksOperations.delete, None),
(VirtualNetworksOperations.get, 'VirtualNetwork'),
(VirtualNetworksOperations.list, '[VirtualNetwork]'),
(VirtualNetworksOperations.list_all, '[VirtualNetwork]'),
])

Просмотреть файл

@ -1,24 +1,24 @@
from msrest import Serializer
from ..commands import command, description, option
from ..commands import command, description
from .._profile import Profile
@command('resource group list')
@description('List resource groups')
# TODO: waiting on Python Azure SDK bug fixes
#@option('--tag-name -g <tagName>', _("the resource group's tag name"))
#@option('--tag-value -g <tagValue>', _("the resource group's tag value"))
#@option('--top -g <number>', _("Top N resource groups to retrieve"))
def list_groups(args, unexpected):
from azure.mgmt.resource.resources import ResourceManagementClient, ResourceManagementClientConfiguration
# @option('--tag-name -g <tagName>', L('the resource group's tag name'))
# @option('--tag-value -g <tagValue>', L('the resource group's tag value'))
# @option('--top -g <number>', L('Top N resource groups to retrieve'))
def list_groups(args, unexpected): #pylint: disable=unused-argument
from azure.mgmt.resource.resources import ResourceManagementClient, \
ResourceManagementClientConfiguration
from azure.mgmt.resource.resources.models import ResourceGroup, ResourceGroupFilter
rmc = ResourceManagementClient(ResourceManagementClientConfiguration(*Profile().get_login_credentials()))
rmc = ResourceManagementClient(
ResourceManagementClientConfiguration(*Profile().get_login_credentials()))
# TODO: waiting on Python Azure SDK bug fixes
#group_filter = ResourceGroupFilter(args.get('tag-name'), args.get('tag-value'))
#groups = rmc.resource_groups.list(filter=None, top=args.get('top'))
groups = rmc.resource_groups.list()
serializable = Serializer().serialize_data(groups, "[ResourceGroup]")
return serializable

Просмотреть файл

@ -1,14 +1,14 @@
from msrest import Serializer
from ..main import SESSION
from msrest import Serializer
from ..commands import command, description, option
from ._command_creation import get_service_client
from .._logging import logger
from .._locale import L
@command('storage account list')
@description(_('List storage accounts'))
@option('--resource-group -g <resourceGroup>', _('the resource group name'))
@option('--subscription -s <id>', _('the subscription id'))
def list_accounts(args, unexpected):
@description(L('List storage accounts'))
@option('--resource-group -g <resourceGroup>', L('the resource group name'))
@option('--subscription -s <id>', L('the subscription id'))
def list_accounts(args, unexpected): #pylint: disable=unused-argument
from azure.mgmt.storage import StorageManagementClient, StorageManagementClientConfiguration
from azure.mgmt.storage.models import StorageAccount
from msrestazure.azure_active_directory import UserPassCredentials
@ -26,10 +26,7 @@ def list_accounts(args, unexpected):
@command('storage account check')
@option('--account-name <name>')
def checkname(args, unexpected):
def checkname(args, unexpected): #pylint: disable=unused-argument
from azure.mgmt.storage import StorageManagementClient, StorageManagementClientConfiguration
smc = get_service_client(StorageManagementClient, StorageManagementClientConfiguration)
logger.warn(smc.storage_accounts.check_name_availability(args.account_name))
logger.warning(smc.storage_accounts.check_name_availability(args.account_name))

Просмотреть файл

@ -0,0 +1,127 @@
from azure.mgmt.compute import ComputeManagementClient, ComputeManagementClientConfiguration
from azure.mgmt.compute.operations import (AvailabilitySetsOperations,
VirtualMachineExtensionImagesOperations,
VirtualMachineExtensionsOperations,
VirtualMachineImagesOperations,
UsageOperations,
VirtualMachineSizesOperations,
VirtualMachinesOperations,
VirtualMachineScaleSetsOperations,
VirtualMachineScaleSetVMsOperations)
from ._command_creation import get_service_client
from ..commands import _auto_command
def _compute_client_factory():
return get_service_client(ComputeManagementClient, ComputeManagementClientConfiguration)
# pylint: disable=line-too-long
_auto_command.operation_builder("vm",
"availabilityset",
"availability_sets",
_compute_client_factory,
[
(AvailabilitySetsOperations.delete, None),
(AvailabilitySetsOperations.get, 'AvailabilitySet'),
(AvailabilitySetsOperations.list, '[AvailabilitySet]'),
(AvailabilitySetsOperations.list_available_sizes, '[VirtualMachineSize]')
])
_auto_command.operation_builder("vm",
"machineextensionimages",
"virtual_machine_extension_images",
_compute_client_factory,
[
(VirtualMachineExtensionImagesOperations.get, 'VirtualMachineExtensionImage'),
(VirtualMachineExtensionImagesOperations.list_types, '[VirtualMachineImageResource]'),
(VirtualMachineExtensionImagesOperations.list_versions, '[VirtualMachineImageResource]'),
])
_auto_command.operation_builder("vm",
"extensions",
"virtual_machine_extensions",
_compute_client_factory,
[
(VirtualMachineExtensionsOperations.delete, None),
(VirtualMachineExtensionsOperations.get, 'VirtualMachineExtension'),
])
_auto_command.operation_builder("vm",
"image",
"virtual_machine_images",
_compute_client_factory,
[
(VirtualMachineImagesOperations.get, 'VirtualMachineImage'),
(VirtualMachineImagesOperations.list, '[VirtualMachineImageResource]'),
(VirtualMachineImagesOperations.list_offers, '[VirtualMachineImageResource]'),
(VirtualMachineImagesOperations.list_publishers, '[VirtualMachineImageResource]'),
(VirtualMachineImagesOperations.list_skus, '[VirtualMachineImageResource]'),
])
_auto_command.operation_builder("vm",
"usage",
"usage",
_compute_client_factory,
[
(UsageOperations.list, '[Usage]'),
])
_auto_command.operation_builder("vm",
"size",
"virtual_machine_sizes",
_compute_client_factory,
[
(VirtualMachineSizesOperations.list, '[VirtualMachineSize]'),
])
_auto_command.operation_builder("vm",
"",
"virtual_machines",
_compute_client_factory,
[
(VirtualMachinesOperations.delete, None),
(VirtualMachinesOperations.deallocate, None),
(VirtualMachinesOperations.generalize, None),
(VirtualMachinesOperations.get, 'VirtualMachine'),
(VirtualMachinesOperations.list, '[VirtualMachine]'),
(VirtualMachinesOperations.list_all, '[VirtualMachine]'),
(VirtualMachinesOperations.list_available_sizes, '[VirtualMachineSize]'),
(VirtualMachinesOperations.power_off, None),
(VirtualMachinesOperations.restart, None),
(VirtualMachinesOperations.start, None),
])
_auto_command.operation_builder("vm",
"scaleset",
"virtual_machine_scale_sets",
_compute_client_factory,
[
(VirtualMachineScaleSetsOperations.deallocate, None),
(VirtualMachineScaleSetsOperations.delete, None),
(VirtualMachineScaleSetsOperations.get, 'VirtualMachineScaleSet'),
(VirtualMachineScaleSetsOperations.delete_instances, None),
(VirtualMachineScaleSetsOperations.get_instance_view, 'VirtualMachineScaleSetInstanceView'),
(VirtualMachineScaleSetsOperations.list, '[VirtualMachineScaleSet]'),
(VirtualMachineScaleSetsOperations.list_all, '[VirtualMachineScaleSet]'),
(VirtualMachineScaleSetsOperations.list_skus, '[VirtualMachineScaleSet]'),
(VirtualMachineScaleSetsOperations.power_off, None),
(VirtualMachineScaleSetsOperations.restart, None),
(VirtualMachineScaleSetsOperations.start, None),
(VirtualMachineScaleSetsOperations.update_instances, None),
])
_auto_command.operation_builder("vm",
"vmscaleset",
"virtual_machine_scale_set_vms",
_compute_client_factory,
[
(VirtualMachineScaleSetVMsOperations.deallocate, None),
(VirtualMachineScaleSetVMsOperations.delete, None),
(VirtualMachineScaleSetVMsOperations.get, None),
(VirtualMachineScaleSetVMsOperations.get_instance_view, 'VirtualMachineScaleSetVMInstanceView'),
(VirtualMachineScaleSetVMsOperations.list, '[VirtualMachineScaleSetVM]'),
(VirtualMachineScaleSetVMsOperations.power_off, None),
(VirtualMachineScaleSetVMsOperations.restart, None),
(VirtualMachineScaleSetVMsOperations.start, None),
])

Просмотреть файл

@ -1,7 +1,6 @@
import os
import os
from ._argparse import ArgumentParser
from ._locale import install as locale_install
from ._logging import configure_logging, logger
from ._session import Session
from ._output import OutputProducer
@ -12,17 +11,18 @@ CONFIG = Session()
# SESSION provides read-write session variables
SESSION = Session()
# Load the user's preferred locale from their configuration
LOCALE = CONFIG.get('locale', 'en-US')
locale_install(os.path.join(os.path.dirname(os.path.abspath(__file__)), 'locale', LOCALE))
def main(args):
CONFIG.load(os.path.expanduser('~/az.json'))
SESSION.load(os.path.expanduser('~/az.sess'), max_age=3600)
configure_logging(args, CONFIG)
from ._locale import install as locale_install
locale_install(os.path.join(os.path.dirname(os.path.abspath(__file__)),
'locale',
CONFIG.get('locale', 'en-US')))
parser = ArgumentParser("az")
import azure.cli.commands as commands
@ -36,7 +36,7 @@ def main(args):
else:
# No noun found, so load all commands.
commands.add_to_parser(parser)
try:
result = parser.execute(args)
# Commands can return a dictionary/list of results

Просмотреть файл

@ -0,0 +1,79 @@
import logging
import unittest
from azure.cli.commands._auto_command import (_decorate_command,
_decorate_option)
from azure.cli.commands import _COMMANDS
class Test_autocommand(unittest.TestCase):
@classmethod
def setUpClass(cls):
# Ensure initialization has occurred correctly
import azure.cli.main
logging.basicConfig(level=logging.DEBUG)
@classmethod
def tearDownClass(cls):
logging.shutdown()
def test_raw_register_command(self):
command_name = 'da command'
def testfunc():
return testfunc
# Run test code
_decorate_command(command_name, testfunc)
# Verify
registered_command = _COMMANDS.get(testfunc, None)
self.assertIsNotNone(registered_command)
self.assertFalse('args' in registered_command.keys())
self.assertEqual(registered_command['name'], command_name)
def test_raw_register_command_with_one_option(self):
command_name = 'da command with one arg'
def testfunc():
return testfunc
# Run test code
func = _decorate_command(command_name, testfunc)
spec = '--tre <tre>'
desc = 'Kronor'
func = _decorate_option(spec, desc, func)
# Verify
registered_command = _COMMANDS.get(testfunc, None)
self.assertIsNotNone(registered_command)
self.assertEqual(registered_command['name'], command_name)
self.assertEqual(len(registered_command['args']), 1)
self.assertEqual(registered_command['args'][0], (spec, desc))
def test_load_test_commands(self):
import sys
from azure.cli._argparse import ArgumentParser
from azure.cli.commands import add_to_parser
# sneaky trick to avoid loading any command modules...
sys.modules['azure.cli.commands.test'] = sys
command_name = 'da command with one arg and unexpected'
def testfunc(args, _):
# Check that the argument passing actually works...
self.assertEqual(args['tre'], 'wombat')
return testfunc
# Run test code
func = _decorate_command(command_name, testfunc)
spec = '--tre <tre>'
desc = 'Kronor'
func = _decorate_option(spec, desc, func)
p = ArgumentParser('automcommandtest')
add_to_parser(p, 'test')
result = p.execute(command_name.split(' ') + '--tre wombat'.split(' '))
self.assertEqual(result, func)
if __name__ == '__main__':
unittest.main()