Repository: softlayer/softlayer-python
Pull request #1741 (2022-08-29 20:20:24)

add vs user-access command
[#ussue1739](https://github.com/softlayer/softlayer-python/issues/1739)

![image](https://user-images.githubusercontent.com/22535437/187290897-c78a4f6e-f375-4cfe-8a99-d6579938bc23.png)



Patch:
diff --git a/SoftLayer/CLI/routes.py b/SoftLayer/CLI/routes.py
index 1fd18264..c85858f8 100644
--- a/SoftLayer/CLI/routes.py
+++ b/SoftLayer/CLI/routes.py
@@ -52,6 +52,7 @@
     ('virtual:capacity', 'SoftLayer.CLI.virt.capacity:cli'),
     ('virtual:placementgroup', 'SoftLayer.CLI.virt.placementgroup:cli'),
     ('virtual:migrate', 'SoftLayer.CLI.virt.migrate:cli'),
+    ('virtual:access', 'SoftLayer.CLI.virt.access:cli'),
     ('virtual:monitoring', 'SoftLayer.CLI.virt.monitoring:cli'),
 
     ('dedicatedhost', 'SoftLayer.CLI.dedicatedhost'),
diff --git a/SoftLayer/CLI/virt/access.py b/SoftLayer/CLI/virt/access.py
new file mode 100644
index 00000000..e61ba810
--- /dev/null
+++ b/SoftLayer/CLI/virt/access.py
@@ -0,0 +1,25 @@
+"""Get user access details a virtual server."""
+# :license: MIT, see LICENSE for more details.
+
+import click
+
+import SoftLayer
+from SoftLayer.CLI import environment
+from SoftLayer.CLI import formatting
+
+
+@click.command(cls=SoftLayer.CLI.command.SLCommand, )
+@click.argument('identifier')
+@environment.pass_env
+def cli(env, identifier):
+    """Get user access details a virtual server."""
+
+    vsi = SoftLayer.VSManager(env.client)
+    access_logs = vsi.browser_access_log(identifier)
+
+    table = formatting.KeyValueTable(['Username', 'Source IP', 'Port', 'Date', 'Event', 'Message'])
+    for log in access_logs:
+        table.add_row([log.get('username'), log.get('sourceIp'), log.get('sourcePort'), log.get('createDate'),
+                       log.get('eventType'), log.get('message')])
+
+    env.fout(table)
diff --git a/SoftLayer/fixtures/SoftLayer_Virtual_Guest.py b/SoftLayer/fixtures/SoftLayer_Virtual_Guest.py
index 3c472b8d..762b923a 100644
--- a/SoftLayer/fixtures/SoftLayer_Virtual_Guest.py
+++ b/SoftLayer/fixtures/SoftLayer_Virtual_Guest.py
@@ -317,7 +317,7 @@
                     'item': {'description': '2 GB'},
                     'hourlyRecurringFee': '.06',
                     'recurringFee': '42'
-            },
+                },
             'template': {'maxMemory': 2048}
         },
         {
@@ -920,3 +920,23 @@
     "createDate": "2021-03-22T13:15:31-06:00",
     "id": 1234567
 }
+
+getBrowserConsoleAccessLogs = [
+    {
+        "createDate": "2022-03-07T05:32:23-06:00",
+        "eventType": "CONNECTED",
+        "id": 509636,
+        "message": "User connected",
+        "sourceIp": "44.200.9.0",
+        "sourcePort": 47097,
+        "username": "testuser@ibm.com@ibm.com"
+    },
+    {
+        "createDate": "2022-03-07T07:58:27-06:00",
+        "eventType": "CONNECTED",
+        "id": 509024,
+        "message": "User connected",
+        "sourceIp": "44.200.9.0",
+        "sourcePort": 41185,
+        "username": "testuser@ibm.com"
+    }]
diff --git a/SoftLayer/managers/vs.py b/SoftLayer/managers/vs.py
index d90ab563..0ef09b2e 100644
--- a/SoftLayer/managers/vs.py
+++ b/SoftLayer/managers/vs.py
@@ -1447,3 +1447,14 @@ def attach_portable_storage(self, vs_id, portable_id):
                                   disk_id, id=vs_id)
 
         return result
+
+    def browser_access_log(self, identifier):
+        """A virtual guest’s browser access logs.
+
+        :param int identifier: Virtual server id.
+
+
+        :return: SoftLayer_Virtual_BrowserConsoleAccessLog.
+        """
+        mask = 'createDate,eventType,id,message,sourceIp,sourcePort,username'
+        return self.client.call('SoftLayer_Virtual_Guest', 'getBrowserConsoleAccessLogs', mask=mask, id=identifier)
diff --git a/docs/cli/vs.rst b/docs/cli/vs.rst
index 3dd34a40..c387d00a 100644
--- a/docs/cli/vs.rst
+++ b/docs/cli/vs.rst
@@ -275,6 +275,10 @@ If no timezone is specified, IMS local time (CST) will be assumed, which might n
    :prog: virtual monitoring
    :show-nested:
 
+.. click:: SoftLayer.CLI.virt.access:cli
+   :prog: virtual access
+   :show-nested:
+
 Manages the migration of virutal guests. Supports migrating virtual guests on Dedicated Hosts as well.
 
 Reserved Capacity


Test patch:
diff --git a/tests/CLI/modules/vs/vs_tests.py b/tests/CLI/modules/vs/vs_tests.py
index 50badc25..e71067ef 100644
--- a/tests/CLI/modules/vs/vs_tests.py
+++ b/tests/CLI/modules/vs/vs_tests.py
@@ -345,19 +345,19 @@ def test_dns_sync_both(self, confirm_mock):
                                            'getResourceRecords')
         getResourceRecords.return_value = []
         createAargs = ({
-            'type': 'a',
-            'host': 'vs-test1',
-            'domainId': 12345,  # from SoftLayer_Account::getDomains
-            'data': '172.16.240.2',
-            'ttl': 7200
-        },)
+                           'type': 'a',
+                           'host': 'vs-test1',
+                           'domainId': 12345,  # from SoftLayer_Account::getDomains
+                           'data': '172.16.240.2',
+                           'ttl': 7200
+                       },)
         createPTRargs = ({
-            'type': 'ptr',
-            'host': '2',
-            'domainId': 123456,
-            'data': 'vs-test1.test.sftlyr.ws',
-            'ttl': 7200
-        },)
+                             'type': 'ptr',
+                             'host': '2',
+                             'domainId': 123456,
+                             'data': 'vs-test1.test.sftlyr.ws',
+                             'ttl': 7200
+                         },)
 
         result = self.run_command(['vs', 'dns-sync', '100'])
 
@@ -400,12 +400,12 @@ def test_dns_sync_v6(self, confirm_mock):
             }
         }
         createV6args = ({
-            'type': 'aaaa',
-            'host': 'vs-test1',
-            'domainId': 12345,
-            'data': '2607:f0d0:1b01:0023:0000:0000:0000:0004',
-            'ttl': 7200
-        },)
+                            'type': 'aaaa',
+                            'host': 'vs-test1',
+                            'domainId': 12345,
+                            'data': '2607:f0d0:1b01:0023:0000:0000:0000:0004',
+                            'ttl': 7200
+                        },)
         guest.return_value = test_guest
         result = self.run_command(['vs', 'dns-sync', '--aaaa-record', '100'])
         self.assert_no_fail(result)
@@ -957,3 +957,7 @@ def test_last_transaction_empty(self):
         mock.return_value = vg_return
         result = self.run_command(['vs', 'detail', '100'])
         self.assert_no_fail(result)
+
+    def test_user_access(self):
+        result = self.run_command(['vs', 'access', '100'])
+        self.assert_no_fail(result)
diff --git a/tests/managers/vs/vs_tests.py b/tests/managers/vs/vs_tests.py
index 976a66fd..18c94d7f 100644
--- a/tests/managers/vs/vs_tests.py
+++ b/tests/managers/vs/vs_tests.py
@@ -939,10 +939,10 @@ def test_edit_full(self):
 
         self.assertEqual(result, True)
         args = ({
-            'hostname': 'new-host',
-            'domain': 'new.sftlyr.ws',
-            'notes': 'random notes',
-        },)
+                    'hostname': 'new-host',
+                    'domain': 'new.sftlyr.ws',
+                    'notes': 'random notes',
+                },)
         self.assert_called_with('SoftLayer_Virtual_Guest', 'editObject',
                                 identifier=100,
                                 args=args)
@@ -1325,3 +1325,8 @@ def test_authorize_storage_empty(self):
     def test_authorize_portable_storage(self):
         options = self.vs.attach_portable_storage(1234, 1234567)
         self.assertEqual(1234567, options['id'])
+
+    def test_browser_access_log(self):
+        result = self.vs.browser_access_log(1234)
+        self.assertTrue(result)
+        self.assert_called_with('SoftLayer_Virtual_Guest', 'getBrowserConsoleAccessLogs', identifier=1234)