def run(self, options, arguments): """Executes command. Args: options: Command-line options. arguments: Command-line positional arguments Returns: True if successful. False, otherwise. """ log.debug('broker machines list %(options)s %(arguments)s', dict(options=options, arguments=arguments)) if arguments: log.error('Unexpected arguments. See --help for more options.') return False deployment_name = options.deployment or options.project cam = camapi.CloudAccessManager(project=options.project, scope=camapi.Scope.DEPLOYMENT) deployment = cam.deployments.get(deployment_name) users = camapi.RequestIterator(cam.machines.entitlements.adusers.get, deployment) visitor = UserPrinter() for user in users: visitor.visit(user)
def test_users(local_backend): """Test user endpoints. Args: local_backend: Backend that intercepts requests and returns predictable test data. """ with local_backend.activate() as backend: backend.expect(LocalBackend.GET, camapi.Deployments.url, params=dict(deploymentName='deployment1', showactive='true'), result=dict(total=1, data=[backend.deployments[0]])) backend.expect(LocalBackend.GET, camapi.MachinesEntitlementsADUsers.url, params=dict(deploymentId='d1', userName='******'), result=dict(total=1, data=[backend.users[0]])) backend.expect(LocalBackend.GET, camapi.MachinesEntitlementsADUsers.url, params=dict(deploymentId='d1', userName='******'), result=dict(total=0, data=[])) backend.expect(LocalBackend.GET, camapi.MachinesEntitlementsADUsers.url, params=dict(deploymentId='d1'), result=dict(total=2, data=backend.users)) cam = camapi.CloudAccessManager(token=backend.token) deployment = cam.deployments.get('deployment1') users = cam.machines.entitlements.adusers.get(deployment, userName='******') assert len(users) == 1 assert users[0]['deploymentId'] == 'd1' assert users[0]['userGuid'] == 'guid1' assert users[0]['name'] == 'User One' assert users[0]['userName'] == 'user1' users = cam.machines.entitlements.adusers.get( deployment, userName='******') assert not users users = cam.machines.entitlements.adusers.get(deployment) assert len(users) == 2 assert users[0]['deploymentId'] == 'd1' assert users[0]['userGuid'] == 'guid1' assert users[0]['name'] == 'User One' assert users[0]['userName'] == 'user1' assert users[1]['deploymentId'] == 'd1' assert users[1]['userGuid'] == 'guid2' assert users[1]['name'] == 'User Two' assert users[1]['userName'] == 'user2'
def test_deployments(local_backend): """Test deployments endpoints. Args: local_backend: Backend that intercepts requests and returns predictable test data. """ with local_backend.activate() as backend: backend.expect(LocalBackend.GET, camapi.Deployments.url, params=dict(deploymentName='deployment1', showactive='true'), result=dict(total=1, data=[backend.deployments[0]])) backend.expect(LocalBackend.GET, camapi.Deployments.url, params=dict(deploymentName='non-existent-deployment', showactive='true'), result=dict(total=0, data=[])) backend.expect(LocalBackend.POST, camapi.Deployments.url, data=['deploymentName', 'registrationCode'], result=backend.deployments_post) cam = camapi.CloudAccessManager(token=backend.token) deployment = cam.deployments.get('deployment1') assert deployment['deploymentId'] == 'd1' assert deployment['deploymentName'] == 'deployment1' assert deployment['registrationCode'] == '111AAA' assert deployment['status'] == 'active' deployment = cam.deployments.get('non-existent-deployment') assert not deployment deployment = cam.deployments.post('deployment2', '222BBB') assert deployment['deploymentId'] == 'd2' assert deployment['deploymentName'] == 'deployment2' assert deployment['registrationCode'] == '222BBB' assert deployment['status'] == 'active' backend.expect(LocalBackend.GET, camapi.Deployments.url, params=dict(deploymentName='deployment2', showactive='true'), result=dict(total=1, data=[backend.deployments[1]])) deployment = cam.deployments.get('deployment2') assert deployment['deploymentId'] == 'd2' assert deployment['deploymentName'] == 'deployment2' assert deployment['registrationCode'] == '222BBB' assert deployment['status'] == 'active'
def test_connection_with_scope_autodetect(): """Verify client can gracefully connect and adapt to scope discrepancies. Dynamically changes the scope if the provided credentials have a different scope, e.g. requested CAM-level but the credentials in fact belong to a Deployment-level account. """ with mock.patch('orchestrate.systems.teradici.camapi.open') as mock_open: # Provide in-memory credentials for a Deployment-level account credentials = dict( keyId='deployment_keyId', username='******', apiKey='deployment_apiKey', keyName='deployment_keyName', deploymentId='deployment_deploymentId', tenantId='deployment_tenantId', ) mock_open_read = mock.MagicMock() mock_open_read.return_value = json.dumps(credentials) mock_open.return_value.__enter__.return_value.read = mock_open_read with mock.patch('requests.post') as requests_post: # Return mock authentication token based on credentials payload = dict(data=dict(token='deployment_token', )) requests_post.return_value.json.return_value = payload # Connect # IMPORTANT difference here: We are requesting a CAM-level connection, # but the actual credentials stored in the file are for a Deployment-level # account. It will connect but update the scope to DEPLOYMENT instead of # CAM. cam = camapi.CloudAccessManager(project='test_connection', scope=camapi.Scope.CAM) # Verify it "loaded" file from the right location path = os.path.abspath( os.path.expanduser( '~/.config/teradici/test_connection-cam.json')) mock_open.assert_called_once_with(path, 'r') # Verify it requested a token from the backend requests_post.assert_called_once() assert requests_post.call_args[0] == (camapi.AuthSignin.url, ) # Verify we got a token that matches the actual credentials from disk # which in this test are different on purpose. assert cam.scope == camapi.Scope.DEPLOYMENT assert cam.headers == dict(Authorization='deployment_token')
def test_computers(local_backend): """Test computers endpoints. Args: local_backend: Backend that intercepts requests and returns predictable test data. """ with local_backend.activate() as backend: backend.expect(LocalBackend.GET, camapi.Deployments.url, params=dict(deploymentName='deployment1', showactive='true'), result=dict(total=1, data=[backend.deployments[0]])) backend.expect(LocalBackend.GET, camapi.MachinesEntitlementsADComputers.url, params=dict(deploymentId='d1', computerName='computer1'), result=dict(total=1, data=[backend.computers[0]])) backend.expect(LocalBackend.GET, camapi.MachinesEntitlementsADComputers.url, params=dict(deploymentId='d1', computerName='non-existent-computer'), result=dict(total=0, data=[])) backend.expect(LocalBackend.GET, camapi.MachinesEntitlementsADComputers.url, params=dict(deploymentId='d1'), result=dict(total=2, data=backend.computers)) cam = camapi.CloudAccessManager(token=backend.token) deployment = cam.deployments.get('deployment1') computers = cam.machines.entitlements.adcomputers.get( deployment, computerName='computer1') assert len(computers) == 1 assert computers[0]['deploymentId'] == 'd1' assert computers[0]['computerName'] == 'COMPUTER1' computers = cam.machines.entitlements.adcomputers.get( deployment, computerName='non-existent-computer') assert not computers computers = cam.machines.entitlements.adcomputers.get(deployment) assert len(computers) == 2 assert computers[0]['deploymentId'] == 'd1' assert computers[0]['computerName'] == 'COMPUTER1' assert computers[1]['deploymentId'] == 'd1' assert computers[1]['computerName'] == 'COMPUTER2'
def unassign(self, project, deployment_name, machine_names): """Unassign all users from given machines. Args: project: GCP project. deployment_name: Deployment. machine_names: Machine names. Returns: True if it succeeded. False otherwise. """ log.debug('Locating deployment: %s', deployment_name) cam = camapi.CloudAccessManager(project=project, scope=camapi.Scope.DEPLOYMENT) deployment = cam.deployments.get(deployment_name) # Get machine ids all_machines = [] for machine_name in machine_names: log.debug('Locating machine in CAM: %s', machine_name) machines = cam.machines.get(deployment, machineName=machine_name) if machines: machine = machines[0] log.debug('Found machine %s with ID %s', machine_name, machine['machineId']) all_machines.append(machine) else: message = ( 'Could not locate machine {machine_name}. Check whether it exists' ' and that it was assigned to users. Skipping for now.' ).format(machine_name=machine_name) log.warning(message) # Find all entitlements for all machine ids collected and remove them for machine in all_machines: log.info( 'Locating entitlements for machine %(machineName)s %(machineId)s', machine) entitlements = cam.machines.entitlements.get( deployment, machineName=machine['machineName']) for entitlement in entitlements: log.info('Removing entitlement %(entitlementId)s', entitlement) cam.machines.entitlements.delete(entitlement) return True
def test_request_iterator(local_backend): """Test RequestIterator. Args: local_backend: Backend that intercepts requests and returns predictable test data. """ with local_backend.activate() as backend: backend.expect(LocalBackend.GET, camapi.Deployments.url, params=dict(deploymentName='deployment1', showactive='true'), result=dict(total=1, data=[backend.deployments[0]])) backend.expect(LocalBackend.GET, camapi.MachinesEntitlementsADUsers.url, params=dict(deploymentId='d1', offset=0, limit=1), result=dict(total=1, data=[backend.users[0]])) backend.expect(LocalBackend.GET, camapi.MachinesEntitlementsADUsers.url, params=dict(deploymentId='d1', offset=1, limit=1), result=dict(total=1, data=[backend.users[1]])) backend.expect(LocalBackend.GET, camapi.MachinesEntitlementsADUsers.url, params=dict(deploymentId='d1', offset=2, limit=1), result=dict(total=0, data=[])) cam = camapi.CloudAccessManager(token=backend.token) deployment = cam.deployments.get('deployment1') assert deployment['deploymentId'] == 'd1' assert deployment['deploymentName'] == 'deployment1' assert deployment['registrationCode'] == '111AAA' assert deployment['status'] == 'active' users = camapi.RequestIterator(cam.machines.entitlements.adusers.get, deployment, limit=1) index = 0 for user in users: index += 1 guid = 'guid{}'.format(index) assert user['userGuid'] == guid
def test_connection_with_deployment_service_account(): """Verify client can connect with CAM Deployment-level service account. """ with mock.patch('orchestrate.systems.teradici.camapi.open') as mock_open: # Provide in-memory credentials credentials = dict( keyId='deployment_keyId', username='******', apiKey='deployment_apiKey', keyName='deployment_keyName', deploymentId='deployment_deploymentId', tenantId='deployment_tenantId', ) mock_open_read = mock.MagicMock() mock_open_read.return_value = json.dumps(credentials) mock_open.return_value.__enter__.return_value.read = mock_open_read with mock.patch('requests.post') as requests_post: # Return mock authentication token based on credentials payload = dict(data=dict(token='deployment_token', )) requests_post.return_value.json.return_value = payload # Connect cam = camapi.CloudAccessManager(project='test_connection', scope=camapi.Scope.DEPLOYMENT) # Verify it "loaded" file from the right location path = os.path.abspath( os.path.expanduser( '~/.config/teradici/test_connection-deployment.json')) mock_open.assert_called_once_with(path, 'r') # Verify it requested a token from the backend requests_post.assert_called_once() assert requests_post.call_args[0] == (camapi.AuthSignin.url, ) # Verify we got a token for the requested scope assert cam.scope == camapi.Scope.DEPLOYMENT assert cam.headers == dict(Authorization='deployment_token')
def assign(self, project, zone, deployment_name, machine_name, user_names): """Assign machine to list of users. Args: project: GCP project. zone: GCP zone. deployment_name: Deployment. machine_name: Machine. user_names: List of user names. Currently requires full user name. Returns: True if it succeeded. False otherwise. """ log.debug('Locating deployment: %s', deployment_name) cam = camapi.CloudAccessManager(project=project, scope=camapi.Scope.DEPLOYMENT) deployment = cam.deployments.get(deployment_name) # Get or create machine # https://github.com/GoogleCloudPlatform/solutions-cloud-orchestrate/issues/35 # Use the right case when locating machines by name, otherwise CAM API # requests will fail with 400 errors. Feels like this should be handled by # CAM itself in the backend. But, for the time being guidelines are: # - CAM machines: lowercase # - AD computers: uppercase cam_machine_name = machine_name.lower() ad_computer_name = machine_name.upper() log.debug('Locating machine in CAM: %s', cam_machine_name) machines = cam.machines.get(deployment, machineName=cam_machine_name) if machines: machine = machines[0] else: log.debug('Locating machine in AD: %s', ad_computer_name) computers = cam.machines.entitlements.adcomputers.get( deployment, computerName=ad_computer_name) if computers: log.debug('Creating machine in CAM: %s', cam_machine_name) machine = cam.machines.post(deployment, cam_machine_name, project, zone) else: message = ( 'Could not locate computer {machine_name}. Check whether it exists' ' and that it joined the AD domain.' ).format(machine_name=machine_name) log.error(message) return False # Create entitlement for every user for user_name in user_names: log.debug('Locating user %s', user_name) users = cam.machines.entitlements.adusers.get(deployment, name=user_name) if users: user = users[0] log.info('Assigning %s to %s (%s)', machine_name, user['name'], user['userName']) try: entitlement = cam.machines.entitlements.post(machine, user) except requests.exceptions.HTTPError as exception: if exception.response.status_code == 409: log.info('Machine %s was already assigned to %s. Skipping', machine_name, user_name) continue else: raise message = 'Assigned {machine} to {user} ({user_name}) with id {id}'.format( machine=machine_name, user=user_name, user_name=user['userName'], id=entitlement['entitlementId'], ) log.info(message) else: message = ( 'Could not locate user {user_name}. Check whether it exists in the' ' AD domain. Skipping for now.' ).format(user_name=user_name) log.warning(message) return True
def test_entitlements(local_backend): """Test entitlement endpoints. Args: local_backend: Backend that intercepts requests and returns predictable test data. """ with local_backend.activate() as backend: backend.expect(LocalBackend.GET, camapi.Deployments.url, params=dict(deploymentName='deployment1', showactive='true'), result=dict(total=1, data=[backend.deployments[0]])) backend.expect(LocalBackend.GET, camapi.MachinesEntitlements.url, params=dict(deploymentId='d1', machineName='computer1'), result=dict(total=1, data=[backend.entitlements[0]])) backend.expect(LocalBackend.GET, camapi.MachinesEntitlements.url, params=dict(deploymentId='d1', machineName='non-existent-computer'), result=dict(total=0, data=[])) backend.expect(LocalBackend.GET, camapi.MachinesEntitlements.url, params=dict(deploymentId='d1'), result=dict(total=2, data=backend.entitlements)) cam = camapi.CloudAccessManager(token=backend.token) deployment = cam.deployments.get('deployment1') entitlements = cam.machines.entitlements.get(deployment, machineName='computer1') assert len(entitlements) == 1 assert entitlements[0]['entitlementId'] == 'e1' assert entitlements[0]['deploymentId'] == 'd1' assert entitlements[0]['machineId'] == 'm1' assert entitlements[0]['userGuid'] == 'guid1' assert entitlements[0]['machine']['machineId'] == 'm1' assert entitlements[0]['machine']['machineName'] == 'computer1' entitlements = cam.machines.entitlements.get( deployment, machineName='non-existent-computer') assert not entitlements entitlements = cam.machines.entitlements.get(deployment) assert len(entitlements) == 2 assert entitlements[0]['entitlementId'] == 'e1' assert entitlements[0]['deploymentId'] == 'd1' assert entitlements[0]['machineId'] == 'm1' assert entitlements[0]['userGuid'] == 'guid1' assert entitlements[0]['machine']['machineId'] == 'm1' assert entitlements[0]['machine']['machineName'] == 'computer1' assert entitlements[1]['entitlementId'] == 'e2' assert entitlements[1]['deploymentId'] == 'd1' assert entitlements[1]['machineId'] == 'm2' assert entitlements[1]['userGuid'] == 'guid2' assert entitlements[1]['machine']['machineId'] == 'm2' assert entitlements[1]['machine']['machineName'] == 'computer2' backend.expect(LocalBackend.POST, camapi.MachinesEntitlements.url, data=['deploymentId', 'machineId', 'userGuid'], result=backend.entitlements_post) backend.expect(LocalBackend.POST, camapi.Machines.url, data=[ 'provider', 'machineName', 'deploymentId', 'projectId', 'zone', 'active', 'managed', ], result=backend.machines_post) backend.expect(LocalBackend.GET, camapi.MachinesEntitlementsADUsers.url, params=dict(deploymentId='d1', userName='******'), result=dict(total=1, data=[backend.users[0]])) machine = cam.machines.post(deployment, 'computer2', 'test_project', 'test_zone') users = cam.machines.entitlements.adusers.get(deployment, userName='******') entitlement = cam.machines.entitlements.post(machine, users[0]) assert entitlement['entitlementId'] == 'e3' assert entitlement['deploymentId'] == 'd1' assert entitlement['machineId'] == 'm2' assert entitlement['userGuid'] == 'guid1' entitlements = cam.machines.entitlements.get(deployment) assert len(entitlements) == 3 assert entitlements[2]['entitlementId'] == 'e3' assert entitlements[2]['deploymentId'] == 'd1' assert entitlements[2]['machineId'] == 'm2' assert entitlements[2]['userGuid'] == 'guid1'
def test_machines(local_backend): """Test machines endpoints. Args: local_backend: Backend that intercepts requests and returns predictable test data. """ with local_backend.activate() as backend: backend.expect(LocalBackend.GET, camapi.Deployments.url, params=dict(deploymentName='deployment1', showactive='true'), result=dict(total=1, data=[backend.deployments[0]])) backend.expect(LocalBackend.GET, camapi.Machines.url, params=dict(deploymentId='d1', machineName='computer1'), result=dict(total=1, data=[backend.machines[0]])) backend.expect(LocalBackend.GET, camapi.Machines.url, params=dict(deploymentId='d1', machineName='non-existent-computer'), result=dict(total=0, data=[])) backend.expect(LocalBackend.POST, camapi.Machines.url, data=[ 'provider', 'machineName', 'deploymentId', 'projectId', 'zone', 'active', 'managed', ], result=backend.machines_post) backend.expect(LocalBackend.GET, camapi.Machines.url, params=dict(deploymentId='d1'), result=dict(total=2, data=backend.machines)) cam = camapi.CloudAccessManager(token=backend.token) deployment = cam.deployments.get('deployment1') machines = cam.machines.get(deployment, machineName='computer1') assert len(machines) == 1 assert machines[0]['deploymentId'] == 'd1' assert machines[0]['machineId'] == 'm1' assert machines[0]['machineName'] == 'computer1' assert machines[0]['projectId'] == 'test' assert machines[0]['zone'] == 'us-west2-b' assert machines[0]['active'] assert machines[0]['managed'] machines = cam.machines.get(deployment, machineName='non-existent-computer') assert not machines machine = cam.machines.post(deployment, 'computer2', 'test_project', 'test_zone') assert machine['deploymentId'] == 'd1' assert machine['machineId'] == 'm2' assert machine['machineName'] == 'computer2' assert machine['projectId'] == 'test_project' assert machine['zone'] == 'test_zone' machines = cam.machines.get(deployment) assert len(machines) == 2 assert machines[0]['deploymentId'] == 'd1' assert machines[0]['machineId'] == 'm1' assert machines[0]['machineName'] == 'computer1' assert machines[0]['projectId'] == 'test' assert machines[0]['zone'] == 'us-west2-b' assert machines[1]['deploymentId'] == 'd1' assert machines[1]['machineId'] == 'm2' assert machines[1]['machineName'] == 'computer2' assert machines[1]['projectId'] == 'test_project' assert machines[1]['zone'] == 'test_zone'
def test_connection_with_token(): """Verify client can connect with an API token. """ cam = camapi.CloudAccessManager(token='abc123') assert cam.scope == camapi.Scope.ALL assert cam.headers == dict(Authorization='abc123')
def test_connection_without_any_creddentials(): """Verify client cannot connect without explicitly providing credentials. """ with pytest.raises(RuntimeError): camapi.CloudAccessManager()