Repository: Azure/azure-cli

Issue:
'Circular reference detected' in query on az vm get-instance-view
```
az vm get-instance-view --resource-group 'RGROUP' --name Name --query '{UUU: name, VVV: instanceView.statuses[1].code}'`
```

Throws an execption:

```
Circular reference detected
Traceback (most recent call last):
  File "/opt/az/local/lib/python2.7/site-packages/azure/cli/main.py", line 39, in main
    OutputProducer(formatter=formatter, file=file).out(cmd_result)
  File "/opt/az/local/lib/python2.7/site-packages/azure/cli/core/_output.py", line 107, in out
    output = self.formatter(obj)
  File "/opt/az/local/lib/python2.7/site-packages/azure/cli/core/_output.py", line 39, in format_json
    separators=(',', ': ')) + '\n'
  File "/usr/lib/python2.7/json/__init__.py", line 250, in dumps
    sort_keys=sort_keys, **kw).encode(obj)
  File "/usr/lib/python2.7/json/encoder.py", line 209, in encode
    chunks = list(chunks)
  File "/usr/lib/python2.7/json/encoder.py", line 434, in _iterencode
    for chunk in _iterencode_dict(o, _current_indent_level):
  File "/usr/lib/python2.7/json/encoder.py", line 408, in _iterencode_dict
    for chunk in chunks:
  File "/usr/lib/python2.7/json/encoder.py", line 408, in _iterencode_dict
    for chunk in chunks:
  File "/usr/lib/python2.7/json/encoder.py", line 332, in _iterencode_list
    for chunk in chunks:
  File "/usr/lib/python2.7/json/encoder.py", line 332, in _iterencode_list
    for chunk in chunks:
  File "/usr/lib/python2.7/json/encoder.py", line 332, in _iterencode_list
    for chunk in chunks:
  File "/usr/lib/python2.7/json/encoder.py", line 295, in _iterencode_list
    raise ValueError("Circular reference detected")
ValueError: Circular reference detected
```

There is no Problem when the query is modified to return the same elements in an array:

```
 az vm get-instance-view --resource-group 'RGROUP' --name Name --query '[name, instanceView.statuses[1].code]'
[
  "Name",
  "PowerState/running"
]

``` 

This happens on ubuntu trusty in a vagrant box (2048MB ram):

```
vagrant@vagrant-ubuntu-trusty-64:~$ az --version
azure-cli (0.1.0b9)

acr (0.1.0b9)
acs (0.1.0b9)
appservice (0.1.0b9)
cloud (0.1.0b9)
component (0.1.0b9)
configure (0.1.0b9)
container (0.1.0b9)
context (0.1.0b9)
core (0.1.0b9)
feedback (0.1.0b9)
network (0.1.0b9)
profile (0.1.0b9)
resource (0.1.0b9)
role (0.1.0b9)
storage (0.1.0b9)
vm (0.1.0b9)

Python (Linux) 2.7.6 (default, Jun 22 2015, 17:58:13)
[GCC 4.8.2]

```

Discussion:
mention-bot: @yugangw-msft, thanks for your PR! By analyzing the history of the files in this pull request, we identified @derekbekoe, @johanste and @tjprescott to be potential reviewers.

Patch:
diff --git a/.mention-bot b/.mention-bot
new file mode 100644
index 000000000..0557e188a
--- /dev/null
+++ b/.mention-bot
@@ -0,0 +1,12 @@
+{
+  "maxReviewers: 5,
+  "userBlacklist": ["BurtBiel"],
+  "assignToReviewer": true,
+  "fallbackNotifyForPaths": [
+    {
+      "name": "JasonRShaver",
+      "files": ["doc/**", "**/_help.py"],
+      "skipTeamPrs": false
+    }
+  ]
+}
diff --git a/src/azure-cli-core/azure/cli/core/_output.py b/src/azure-cli-core/azure/cli/core/_output.py
index 9dde28a31..bd79555ce 100644
--- a/src/azure-cli-core/azure/cli/core/_output.py
+++ b/src/azure-cli-core/azure/cli/core/_output.py
@@ -34,7 +34,8 @@ class ComplexEncoder(json.JSONEncoder):
 
 def format_json(obj):
     result = obj.result
-    input_dict = result.__dict__ if hasattr(result, '__dict__') else result
+    #OrderedDict.__dict__ is always '{}', to persist the data, convert to dict first.
+    input_dict = dict(result) if hasattr(result, '__dict__') else result
     return json.dumps(input_dict, indent=2, sort_keys=True, cls=ComplexEncoder,
                       separators=(',', ': ')) + '\n'
 
diff --git a/src/azure-cli-core/azure/cli/core/_profile.py b/src/azure-cli-core/azure/cli/core/_profile.py
index b7796cd4b..ba85481ff 100644
--- a/src/azure-cli-core/azure/cli/core/_profile.py
+++ b/src/azure-cli-core/azure/cli/core/_profile.py
@@ -247,6 +247,39 @@ class Profile(object):
                 str(account[_SUBSCRIPTION_ID]),
                 str(account[_TENANT_ID]))
 
+    #per ask from java sdk
+    def get_expanded_subscription_info(self, subscription_id=None, name=None, password=None):
+        account = self.get_subscription(subscription_id)
+
+        # is the credential created through command like 'create-for-rbac'?
+        if bool(name) and bool(password):
+            result = {}
+            result[_SUBSCRIPTION_ID] = subscription_id or account[_SUBSCRIPTION_ID]
+            result['client'] = name
+            result['password'] = password
+            result[_TENANT_ID] = account[_TENANT_ID]
+            result[_ENVIRONMENT_NAME] = CLOUD.name
+            result['subscriptionName'] = account[_SUBSCRIPTION_NAME]
+        else: #has logged in through cli
+            from copy import deepcopy
+            result = deepcopy(account)
+            user_type = account[_USER_ENTITY].get(_USER_TYPE)
+            if user_type == _SERVICE_PRINCIPAL:
+                result['client'] = account[_USER_ENTITY][_USER_NAME]
+                result['password'] = self._creds_cache.retrieve_secret_of_service_principal(
+                    account[_USER_ENTITY][_USER_NAME])
+            else:
+                result['userName'] = account[_USER_ENTITY][_USER_NAME]
+
+            result.pop(_STATE)
+            result.pop(_USER_ENTITY)
+            result.pop(_IS_DEFAULT_SUBSCRIPTION)
+            result['subscriptionName'] = result.pop(_SUBSCRIPTION_NAME)
+
+        result['subscriptionId'] = result.pop('id')
+        result['endpoints'] = CLOUD.endpoints
+        return result
+
     def get_installation_id(self):
         installation_id = self._storage.get(_INSTALLATION_ID)
         if not installation_id:
@@ -381,6 +414,13 @@ class CredsCache(object):
                                                                     cred[_ACCESS_TOKEN])
         return (token_entry[_TOKEN_ENTRY_TOKEN_TYPE], token_entry[_ACCESS_TOKEN])
 
+    def retrieve_secret_of_service_principal(self, sp_id):
+        matched = [x for x in self._service_principal_creds if sp_id == x[_SERVICE_PRINCIPAL_ID]]
+        if not matched:
+            raise CLIError("No matched service principal found")
+        cred = matched[0]
+        return cred[_ACCESS_TOKEN]
+
     def _load_creds(self):
         if self.adal_token_cache is not None:
             return self.adal_token_cache
diff --git a/src/command_modules/azure-cli-component/azure/cli/command_modules/component/_params.py b/src/command_modules/azure-cli-component/azure/cli/command_modules/component/_params.py
index dbb2b369c..ea399981f 100644
--- a/src/command_modules/azure-cli-component/azure/cli/command_modules/component/_params.py
+++ b/src/command_modules/azure-cli-component/azure/cli/command_modules/component/_params.py
@@ -10,5 +10,4 @@ register_cli_argument('component', 'component_name', options_list=('--name', '-n
 register_cli_argument('component', 'link', options_list=('--link', '-l'), help='If a url or path to an html file, parse for links to archives. If local path or file:// url that\'s a directory, then look for archives in the directory listing.')
 register_cli_argument('component', 'private', help='Include packages from a private server.', action='store_true')
 register_cli_argument('component', 'pre', help='Include pre-release versions', action='store_true')
-register_cli_argument('component', 'show_logs', help='Show logs from packaging installing/updating', action='store_true')
 register_cli_argument('component', 'additional_components', options_list=('--add',), nargs='+', help='The names of additional components to install (space separated)')
diff --git a/src/command_modules/azure-cli-component/azure/cli/command_modules/component/custom.py b/src/command_modules/azure-cli-component/azure/cli/command_modules/component/custom.py
index 1dbd60554..e6cca2b5d 100644
--- a/src/command_modules/azure-cli-component/azure/cli/command_modules/component/custom.py
+++ b/src/command_modules/azure-cli-component/azure/cli/command_modules/component/custom.py
@@ -21,7 +21,7 @@ def list_components():
                    for dist in pip.get_installed_distributions(local_only=True)
                    if dist.key.startswith(COMPONENT_PREFIX)], key=lambda x: x['name'])
 
-def remove(component_name, show_logs=False):
+def remove(component_name):
     """ Remove a component """
     import pip
     full_component_name = COMPONENT_PREFIX + component_name
@@ -29,7 +29,6 @@ def remove(component_name, show_logs=False):
                   if dist.key == full_component_name])
     if found:
         options = ['--isolated', '--yes']
-        options += [] if show_logs else ['--quiet']
         pip_args = ['uninstall'] + options + ['--disable-pip-version-check', full_component_name]
         _run_pip(pip, pip_args)
     else:
@@ -53,13 +52,11 @@ def _run_pip(pip, pip_exec_args):
         raise CLIError('An error occurred. Run command with --debug for more information.\n'
                        'If executing az with sudo, you may want sudo\'s -E and -H flags.')
 
-def _install_or_update(package_list, link, private, pre, show_logs=False):
+def _install_or_update(package_list, link, private, pre):
     import pip
     options = ['--isolated', '--disable-pip-version-check', '--upgrade']
     if pre:
         options.append('--pre')
-    if not show_logs:
-        options.append('--quiet')
     pkg_index_options = ['--find-links', link] if link else []
     if private:
         package_index_url = az_config.get('component', 'package_index_url', fallback=None)
@@ -74,7 +71,7 @@ def _install_or_update(package_list, link, private, pre, show_logs=False):
     pip_args = ['install'] + options + package_list + pkg_index_options
     _run_pip(pip, pip_args)
 
-def update(private=False, pre=False, link=None, additional_components=None, show_logs=False):
+def update(private=False, pre=False, link=None, additional_components=None):
     """ Update the CLI and all installed components """
     import pip
     # Update the CLI itself
@@ -86,4 +83,4 @@ def update(private=False, pre=False, link=None, additional_components=None, show
     if additional_components:
         for c in additional_components:
             package_list += [COMPONENT_PREFIX + c]
-    _install_or_update(package_list, link, private, pre, show_logs=show_logs)
+    _install_or_update(package_list, link, private, pre)
diff --git a/src/command_modules/azure-cli-container/azure/cli/command_modules/container/custom.py b/src/command_modules/azure-cli-container/azure/cli/command_modules/container/custom.py
index 23825d586..c92358966 100644
--- a/src/command_modules/azure-cli-container/azure/cli/command_modules/container/custom.py
+++ b/src/command_modules/azure-cli-container/azure/cli/command_modules/container/custom.py
@@ -310,6 +310,8 @@ def _get_remote_url():
             "A default remote was not found for the current folder. \
             Please run this command in a git repository folder with \
             an 'origin' remote or specify a remote using '--remote-url'")
+    except CalledProcessError as e:
+        raise CLIError(e)
     return remote_url.decode()
 
 def _check_registry_information(registry_name, registry_resource_id):
diff --git a/src/command_modules/azure-cli-profile/azure/cli/command_modules/profile/_params.py b/src/command_modules/azure-cli-profile/azure/cli/command_modules/profile/_params.py
index 81437a26c..107bc702c 100644
--- a/src/command_modules/azure-cli-profile/azure/cli/command_modules/profile/_params.py
+++ b/src/command_modules/azure-cli-profile/azure/cli/command_modules/profile/_params.py
@@ -25,3 +25,4 @@ register_cli_argument('logout', 'username', help='account user, if missing, logo
 
 register_cli_argument('account', 'subscription', help='Name or ID of subscription.', completer=get_subscription_id_list)
 register_cli_argument('account list', 'list_all', options_list=('--all',), help='List all subscriptions across all cloud environments', action='store_true')
+register_cli_argument('account show', 'expanded_view', action='store_true', help="display more information like service principal's password and cloud environments")
diff --git a/src/command_modules/azure-cli-profile/azure/cli/command_modules/profile/custom.py b/src/command_modules/azure-cli-profile/azure/cli/command_modules/profile/custom.py
index 880b19660..dbb031dd2 100644
--- a/src/command_modules/azure-cli-profile/azure/cli/command_modules/profile/custom.py
+++ b/src/command_modules/azure-cli-profile/azure/cli/command_modules/profile/custom.py
@@ -27,9 +27,12 @@ def list_subscriptions(list_all=False): # pylint: disable=redefined-builtin
         sub['cloudName'] = sub.pop('environmentName', None)
     return [sub for sub in subscriptions if list_all or sub['cloudName'] == CLOUD.name]
 
-def show_subscription(subscription=None):
+def show_subscription(subscription=None, expanded_view=None):
     profile = Profile()
-    return profile.get_subscription(subscription)
+    if not expanded_view:
+        return profile.get_subscription(subscription)
+    else:
+        return profile.get_expanded_subscription_info(subscription)
 
 def set_active_subscription(subscription):
     '''Set the current subscription'''
diff --git a/src/command_modules/azure-cli-role/azure/cli/command_modules/role/_params.py b/src/command_modules/azure-cli-role/azure/cli/command_modules/role/_params.py
index cd259ee3c..687aa9b1e 100644
--- a/src/command_modules/azure-cli-role/azure/cli/command_modules/role/_params.py
+++ b/src/command_modules/azure-cli-role/azure/cli/command_modules/role/_params.py
@@ -34,6 +34,7 @@ register_cli_argument('ad sp create', 'identifier', options_list=('--id',), help
 register_cli_argument('ad sp create-for-rbac', 'name', name_arg_type)
 register_cli_argument('ad sp create-for-rbac', 'years', type=int)
 register_cli_argument('ad sp create-for-rbac', 'scopes', nargs='+')
+register_cli_argument('ad sp create-for-rbac', 'expanded_view', action='store_true', help='Once created, display more information like subscription and cloud environments')
 register_cli_argument('ad sp reset-credentials', 'name', name_arg_type)
 register_cli_argument('ad sp reset-credentials', 'years', type=int)
 
diff --git a/src/command_modules/azure-cli-role/azure/cli/command_modules/role/custom.py b/src/command_modules/azure-cli-role/azure/cli/command_modules/role/custom.py
index 37a5924e4..5b6d472cd 100644
--- a/src/command_modules/azure-cli-role/azure/cli/command_modules/role/custom.py
+++ b/src/command_modules/azure-cli-role/azure/cli/command_modules/role/custom.py
@@ -434,8 +434,8 @@ def _resolve_service_principal(client, identifier):
     except ValueError:
         raise CLIError("service principal '{}' doesn't exist".format(identifier))
 
-def create_service_principal_for_rbac(name=None, password=None, years=1,
-                                      scopes=None, role=None):
+def create_service_principal_for_rbac(name=None, password=None, years=1, #pylint:disable=too-many-arguments
+                                      scopes=None, role=None, expanded_view=None):
     '''create a service principal that can access or modify resources
     :param str name: an unique uri. If missing, the command will generate one.
     :param str password: the password used to login. If missing, command will generate one.
@@ -472,12 +472,19 @@ def create_service_principal_for_rbac(name=None, password=None, years=1,
         for scope in scopes:
             _create_role_assignment(role, oid, None, scope, ocp_aad_session_key=session_key)
 
-    return {
-        'appId': aad_application.app_id,
-        'password': password,
-        'name': name,
-        'tenant': client.config.tenant_id
-        }
+    if expanded_view:
+        from azure.cli.core._profile import Profile
+        profile = Profile()
+        result = profile.get_expanded_subscription_info(scopes[0].split('/')[2] if scopes else None,
+                                                        aad_application.app_id, password)
+    else:
+        result = {
+            'appId': aad_application.app_id,
+            'password': password,
+            'name': name,
+            'tenant': client.config.tenant_id
+            }
+    return result
 
 def reset_service_principal_credential(name, password=None, years=1):
     '''reset credential, on expiration or you forget it.


Test patch:
diff --git a/src/azure-cli-core/azure/cli/core/tests/test_output.py b/src/azure-cli-core/azure/cli/core/tests/test_output.py
index 19babaf83..37ae2bbf0 100644
--- a/src/azure-cli-core/azure/cli/core/tests/test_output.py
+++ b/src/azure-cli-core/azure/cli/core/tests/test_output.py
@@ -40,6 +40,19 @@ class TestOutput(unittest.TestCase):
   "active": true,
   "id": "0b1f6472"
 }
+"""))
+   
+    def test_out_json_from_ordered_dict(self):
+        """
+        The JSON output when the input is OrderedDict should be serialized to JSON
+        """
+        output_producer = OutputProducer(formatter=format_json, file=self.io)
+        output_producer.out(CommandResultItem(OrderedDict({'active': True, 'id': '0b1f6472'})))
+        self.assertEqual(util.normalize_newlines(self.io.getvalue()), util.normalize_newlines(
+"""{
+  "active": true,
+  "id": "0b1f6472"
+}
 """))
 
     def test_out_json_byte(self):
diff --git a/src/azure-cli-core/azure/cli/core/tests/test_profile.py b/src/azure-cli-core/azure/cli/core/tests/test_profile.py
index 36268067c..609e6b3f2 100644
--- a/src/azure-cli-core/azure/cli/core/tests/test_profile.py
+++ b/src/azure-cli-core/azure/cli/core/tests/test_profile.py
@@ -12,7 +12,7 @@ from azure.mgmt.resource.subscriptions.models import (SubscriptionState, Subscri
 from azure.cli.core._profile import Profile, CredsCache, SubscriptionFinder, CLOUD
 from azure.cli.core._util import CLIError
 
-class Test_Profile(unittest.TestCase):
+class Test_Profile(unittest.TestCase): #pylint: disable=too-many-public-methods
 
     @classmethod
     def setUpClass(cls):
@@ -183,6 +183,58 @@ class Test_Profile(unittest.TestCase):
         self.assertEqual(sub_id, profile.get_subscription(subscription=sub_id)['id'])
         self.assertRaises(CLIError, profile.get_subscription, "random_id")
 
+    def test_get_expanded_subscription_info(self):
+        storage_mock = {'subscriptions': None}
+        profile = Profile(storage_mock)
+
+        consolidated = Profile._normalize_properties(self.user1,
+                                                     [self.subscription1],
+                                                     False)
+        profile._set_subscriptions(consolidated)
+        sub_id = self.id1.split('/')[-1]
+
+        #testing dump of existing logged in account
+        extended_info = profile.get_expanded_subscription_info()
+        self.assertEqual(self.user1, extended_info['userName'])
+        self.assertEqual(sub_id, extended_info['subscriptionId'])
+        self.assertEqual(self.display_name1, extended_info['subscriptionName'])
+        self.assertEqual('https://login.microsoftonline.com',
+                         extended_info['endpoints'].active_directory)
+
+        #testing dump of service principal by 'create-for-rbac'
+        extended_info = profile.get_expanded_subscription_info(name='great-sp',
+                                                               password='verySecret-')
+        self.assertEqual(sub_id, extended_info['subscriptionId'])
+        self.assertEqual(self.display_name1, extended_info['subscriptionName'])
+        self.assertEqual('great-sp', extended_info['client'])
+        self.assertEqual('https://login.microsoftonline.com',
+                         extended_info['endpoints'].active_directory)
+
+    @mock.patch('adal.AuthenticationContext', autospec=True)
+    def test_get_expanded_subscription_info_for_logged_in_service_principal(self,
+                                                                            mock_auth_context):
+        mock_auth_context.acquire_token_with_client_credentials.return_value = self.token_entry1
+        mock_arm_client = mock.MagicMock()
+        mock_arm_client.subscriptions.list.return_value = [self.subscription1]
+        finder = SubscriptionFinder(lambda _, _2: mock_auth_context,
+                                    None,
+                                    lambda _: mock_arm_client)
+
+        storage_mock = {'subscriptions': []}
+        profile = Profile(storage_mock)
+        profile._management_resource_uri = 'https://management.core.windows.net/'
+        profile._subscription_finder = finder
+        profile.find_subscriptions_on_login(False, '1234', 'my-secret', True, self.tenant_id)
+        #action
+        extended_info = profile.get_expanded_subscription_info()
+        #assert
+        self.assertEqual(self.id1.split('/')[-1], extended_info['subscriptionId'])
+        self.assertEqual(self.display_name1, extended_info['subscriptionName'])
+        self.assertEqual('1234', extended_info['client'])
+        self.assertEqual('https://login.microsoftonline.com',
+                         extended_info['endpoints'].active_directory)
+
+
     @mock.patch('azure.cli.core._profile._load_tokens_from_file', autospec=True)
     def test_get_current_account_user(self, mock_read_cred_file):
         #setup