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 - pip install -r requirements.txt
script: script:
- export PYTHONPATH=$PATHONPATH:./src - 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> <LaunchProvider>Standard Python launcher</LaunchProvider>
<InterpreterId>{1dd9c42b-5980-42ce-a2c3-46d3bf0eede4}</InterpreterId> <InterpreterId>{1dd9c42b-5980-42ce-a2c3-46d3bf0eede4}</InterpreterId>
<InterpreterVersion>3.5</InterpreterVersion> <InterpreterVersion>3.5</InterpreterVersion>
<CommandLineArguments>
</CommandLineArguments>
<EnableNativeCodeDebugging>False</EnableNativeCodeDebugging>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' == 'Debug'" /> <PropertyGroup Condition="'$(Configuration)' == 'Debug'" />
<PropertyGroup Condition="'$(Configuration)' == 'Release'" /> <PropertyGroup Condition="'$(Configuration)' == 'Release'" />
@ -25,15 +28,23 @@
<Compile Include="azure\cli\commands\account.py" /> <Compile Include="azure\cli\commands\account.py" />
<Compile Include="azure\cli\commands\login.py" /> <Compile Include="azure\cli\commands\login.py" />
<Compile Include="azure\cli\commands\logout.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\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\commands\__init__.py" />
<Compile Include="azure\cli\main.py" /> <Compile Include="azure\cli\main.py" />
<Compile Include="azure\cli\tests\test_argparse.py"> <Compile Include="azure\cli\tests\test_argparse.py">
<SubType>Code</SubType> <SubType>Code</SubType>
</Compile> </Compile>
<Compile Include="azure\cli\tests\test_autocommand.py">
<SubType>Code</SubType>
</Compile>
<Compile Include="azure\cli\tests\test_connection_verify.py"> <Compile Include="azure\cli\tests\test_connection_verify.py">
<SubType>Code</SubType> <SubType>Code</SubType>
</Compile> </Compile>
<Compile Include="azure\cli\tests\test_output.py" />
<Compile Include="azure\cli\tests\test_profile.py" /> <Compile Include="azure\cli\tests\test_profile.py" />
<Compile Include="azure\cli\_argparse.py" /> <Compile Include="azure\cli\_argparse.py" />
<Compile Include="azure\cli\commands\_command_creation.py"> <Compile Include="azure\cli\commands\_command_creation.py">
@ -42,7 +53,9 @@
<Compile Include="azure\cli\_debug.py"> <Compile Include="azure\cli\_debug.py">
<SubType>Code</SubType> <SubType>Code</SubType>
</Compile> </Compile>
<Compile Include="azure\cli\_locale.py" />
<Compile Include="azure\cli\_logging.py" /> <Compile Include="azure\cli\_logging.py" />
<Compile Include="azure\cli\_output.py" />
<Compile Include="azure\cli\_profile.py" /> <Compile Include="azure\cli\_profile.py" />
<Compile Include="azure\cli\_session.py"> <Compile Include="azure\cli\_session.py">
<SubType>Code</SubType> <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 azure==2.0.0a1
mock==1.3.0 mock==1.3.0
pylint==1.5.4

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

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

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

@ -1,12 +1,14 @@
import os import os
from ._logging import logger from ._logging import logger
DISABLE_VERIFY_VARIABLE_NAME = "AZURE_CLI_DISABLE_CONNECTION_VERIFICATION" DISABLE_VERIFY_VARIABLE_NAME = "AZURE_CLI_DISABLE_CONNECTION_VERIFICATION"
def allow_debug_connection(client): def allow_debug_connection(client):
if should_disable_connection_verify(): if should_disable_connection_verify():
logger.warn("Connection verification disabled by environment variable %s", DISABLE_VERIFY_VARIABLE_NAME) logger.warning("Connection verification disabled by environment variable %s",
DISABLE_VERIFY_VARIABLE_NAME)
client.config.connection.verify = False client.config.connection.verify = False
def should_disable_connection_verify(): def should_disable_connection_verify():
return bool(os.environ.get(DISABLE_VERIFY_VARIABLE_NAME)) 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): def install(locale_dir):
mapping = [] 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: for i in f:
if not i or i.startswith('#') or not i.strip(): if not i or i.startswith('#') or not i.strip():
continue continue
@ -13,18 +18,14 @@ def install(locale_dir):
mapping.append((i[5:].strip(), None)) mapping.append((i[5:].strip(), None))
else: else:
mapping[-1] = (mapping[-1][0], i.strip()) mapping[-1] = (mapping[-1][0], i.strip())
translations = dict(mapping) globals()['_translations'] = dict(mapping)
def _(key): globals()['_locale_dir'] = locale_dir
return translations.get(key) or '<NO_TRANSLATION:{}>'.format(key)
_.locale_dir = locale_dir
__builtins__['_'] = _
def get_file(name): def get_file(name):
try: try:
src = _.locale_dir src = _locale_dir
except (NameError, AttributeError): except (NameError, AttributeError):
raise RuntimeError("localizations not installed") raise RuntimeError("localizations not installed")
return os.path.join(src, name) return os.path.join(src, name)

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

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

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

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

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

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

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

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

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

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

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

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

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

@ -1,21 +1,22 @@
from .._profile import Profile from .._profile import Profile
from ..commands import command, description, option from ..commands import command, description, option
from .._locale import L
@command('account list') @command('account list')
@description(_('List the imported subscriptions.')) @description(L('List the imported subscriptions.'))
def list_subscriptions(args, unexpected): def list_subscriptions(args, unexpected): #pylint: disable=unused-argument
profile = Profile() profile = Profile()
subscriptions = profile.load_subscriptions() subscriptions = profile.load_subscriptions()
return subscriptions return subscriptions
@command('account set') @command('account set')
@description(_('Set the current subscription')) @description(L('Set the current subscription'))
@option('--subscription-id -n <subscription-id>', _('Subscription Id, unique name also works.')) @option('--subscription-id -n <subscription-id>', L('Subscription Id, unique name also works.'))
def set_active_subscription(args, unexpected): def set_active_subscription(args, unexpected): #pylint: disable=unused-argument
id = args.get('subscription-id') subscription_id = args.get('subscription-id')
if not 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 = 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, \ from azure.mgmt.resource.subscriptions import SubscriptionClient, \
SubscriptionClientConfiguration SubscriptionClientConfiguration
from msrest import Serializer
from .._profile import Profile from .._profile import Profile
from ..commands import command, description, option from ..commands import command, description, option
from .._debug import should_disable_connection_verify from .._debug import should_disable_connection_verify
from .._locale import L
CLIENT_ID = '04b07795-8ddb-461a-bbee-02f9e1bf7b46' CLIENT_ID = '04b07795-8ddb-461a-bbee-02f9e1bf7b46'
@command('login') @command('login')
@description(_('log in to an Azure subscription using Active Directory Organization Id')) @description(L('log in to an Azure subscription using Active Directory Organization Id'))
@option('--username -u <username>', _('organization Id. Microsoft Account is not yet supported.')) @option('--username -u <username>', L('organization Id. Microsoft Account is not yet supported.'))
@option('--password -p <password>', _('user password, will prompt if not given.')) @option('--password -p <password>', L('user password, will prompt if not given.'))
def login(args, unexpected): def login(args, unexpected): #pylint: disable=unused-argument
username = args.get('username') username = args.get('username')
password = args.get('password') password = args.get('password')
if not password: if not password:
import getpass 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)) client = SubscriptionClient(SubscriptionClientConfiguration(credentials))
subscriptions = client.subscriptions.list() subscriptions = client.subscriptions.list()
if not subscriptions: 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]") serializable = Serializer().serialize_data(subscriptions, "[Subscription]")
#keep useful properties and not json serializable #keep useful properties and not json serializable
profile = Profile() profile = Profile()
consolidated = Profile.normalize_properties(username, subscriptions) consolidated = Profile.normalize_properties(username, subscriptions)
profile.set_subscriptions(consolidated, credentials.token['access_token']) profile.set_subscriptions(consolidated, credentials.token['access_token'])

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

@ -1,13 +1,14 @@
from .._profile import Profile from .._profile import Profile
from ..commands import command, description, option from ..commands import command, description, option
from .._locale import L
@command('logout') @command('logout')
@description(_('Log out from Azure subscription using Active Directory.')) @description(L('Log out from Azure subscription using Active Directory.'))
@option('--username -u <username>', _('User name used to log out from Azure Active Directory.')) @option('--username -u <username>', L('User name used to log out from Azure Active Directory.'))
def logout(args, unexpected): def logout(args, unexpected): #pylint: disable=unused-argument
username = args.get('username') username = args.get('username')
if not 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 = Profile()
profile.logout(username) 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 msrest import Serializer
from ..commands import command, description
from ..commands import command, description, option
from .._profile import Profile from .._profile import Profile
@command('resource group list') @command('resource group list')
@description('List resource groups') @description('List resource groups')
# TODO: waiting on Python Azure SDK bug fixes # TODO: waiting on Python Azure SDK bug fixes
#@option('--tag-name -g <tagName>', _("the resource group's tag name")) # @option('--tag-name -g <tagName>', L('the resource group's tag name'))
#@option('--tag-value -g <tagValue>', _("the resource group's tag value")) # @option('--tag-value -g <tagValue>', L('the resource group's tag value'))
#@option('--top -g <number>', _("Top N resource groups to retrieve")) # @option('--top -g <number>', L('Top N resource groups to retrieve'))
def list_groups(args, unexpected): def list_groups(args, unexpected): #pylint: disable=unused-argument
from azure.mgmt.resource.resources import ResourceManagementClient, ResourceManagementClientConfiguration from azure.mgmt.resource.resources import ResourceManagementClient, \
ResourceManagementClientConfiguration
from azure.mgmt.resource.resources.models import ResourceGroup, ResourceGroupFilter 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 # TODO: waiting on Python Azure SDK bug fixes
#group_filter = ResourceGroupFilter(args.get('tag-name'), args.get('tag-value')) #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(filter=None, top=args.get('top'))
groups = rmc.resource_groups.list() groups = rmc.resource_groups.list()
serializable = Serializer().serialize_data(groups, "[ResourceGroup]") serializable = Serializer().serialize_data(groups, "[ResourceGroup]")
return serializable return serializable

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

@ -1,14 +1,14 @@
from msrest import Serializer from msrest import Serializer
from ..main import SESSION
from ..commands import command, description, option from ..commands import command, description, option
from ._command_creation import get_service_client from ._command_creation import get_service_client
from .._logging import logger
from .._locale import L
@command('storage account list') @command('storage account list')
@description(_('List storage accounts')) @description(L('List storage accounts'))
@option('--resource-group -g <resourceGroup>', _('the resource group name')) @option('--resource-group -g <resourceGroup>', L('the resource group name'))
@option('--subscription -s <id>', _('the subscription id')) @option('--subscription -s <id>', L('the subscription id'))
def list_accounts(args, unexpected): def list_accounts(args, unexpected): #pylint: disable=unused-argument
from azure.mgmt.storage import StorageManagementClient, StorageManagementClientConfiguration from azure.mgmt.storage import StorageManagementClient, StorageManagementClientConfiguration
from azure.mgmt.storage.models import StorageAccount from azure.mgmt.storage.models import StorageAccount
from msrestazure.azure_active_directory import UserPassCredentials from msrestazure.azure_active_directory import UserPassCredentials
@ -26,10 +26,7 @@ def list_accounts(args, unexpected):
@command('storage account check') @command('storage account check')
@option('--account-name <name>') @option('--account-name <name>')
def checkname(args, unexpected): def checkname(args, unexpected): #pylint: disable=unused-argument
from azure.mgmt.storage import StorageManagementClient, StorageManagementClientConfiguration from azure.mgmt.storage import StorageManagementClient, StorageManagementClientConfiguration
smc = get_service_client(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 ._argparse import ArgumentParser
from ._locale import install as locale_install
from ._logging import configure_logging, logger from ._logging import configure_logging, logger
from ._session import Session from ._session import Session
from ._output import OutputProducer from ._output import OutputProducer
@ -12,17 +11,18 @@ CONFIG = Session()
# SESSION provides read-write session variables # SESSION provides read-write session variables
SESSION = Session() 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): def main(args):
CONFIG.load(os.path.expanduser('~/az.json')) CONFIG.load(os.path.expanduser('~/az.json'))
SESSION.load(os.path.expanduser('~/az.sess'), max_age=3600) SESSION.load(os.path.expanduser('~/az.sess'), max_age=3600)
configure_logging(args, CONFIG) 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") parser = ArgumentParser("az")
import azure.cli.commands as commands import azure.cli.commands as commands
@ -36,7 +36,7 @@ def main(args):
else: else:
# No noun found, so load all commands. # No noun found, so load all commands.
commands.add_to_parser(parser) commands.add_to_parser(parser)
try: try:
result = parser.execute(args) result = parser.execute(args)
# Commands can return a dictionary/list of results # 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()