0

I have a script that pulls server info from GCP that works fine:

import googleapiclient.discovery
compute = googleapiclient.discovery.build('compute', 'v1')
project_id = 'company-dev'
zone = 'us-east1-d'
def list_instances(compute,project_id,date):
    zone = 'us-east1-d'
    delete_from_collection(date)
    result = compute.instances().list(project=project_id, zone=zone).execute()
    items = result['items']
    for item in items:
        instance_id = item['id']
        name = item['name']
        timestamp = item['creationTimestamp']
        private_ip = item['networkInterfaces'][0]['networkIP']
        #network = item['networkInterfaces'][0]['network']
        instance_dict = {'Instance ID': instance_id, 'Name': name,'Network Interfaces': private_ip,'Timestamp': timestamp,}
        #print(instance_dict)
        insert_coll(instance_dict,date)

But I need to alter this function so that it also loops through a list of zones. And when I add the zones list and another for loop for the zones list it loses sight of a variable called items = result['items'] that works in the previous version.

This function with the added for loop:

def list_instances(compute,project_id,date):
    delete_from_collection(date)
    zones = ['us-east1-b','us-east1-c','us-east1-d','us-east4-c','us-east4-b','us-east4-a']
    for zone in zones:
        result = compute.instances().list(project=project_id, zone=zone).execute()
        items = result['items']
        for item in items:
            instance_id = item['id']
            name = item['name']
            timestamp = item['creationTimestamp']
            private_ip = item['networkInterfaces'][0]['networkIP']
            #network = item['networkInterfaces'][0]['network']
            instance_dict = {'Instance ID': instance_id, 'Name': name,'Network Interfaces': private_ip,'Timestamp': timestamp,}
            #print(instance_dict)
            insert_coll(instance_dict,date)

Produces the following error:

Traceback (most recent call last):
  File "gcp_list_instances.py", line 379, in <module>
    main()
  File "gcp_list_instances.py", line 370, in main
    list_instances(compute,project_id,date)
  File "gcp_list_instances.py", line 151, in list_instances
    items = result['items']
KeyError: 'items'

Why does the inner for loop lose track of the items = result['items'] variable? And how can I get the inner for loop to recognize that variable?

7
  • result has no key items. That have nothing to do with the inner loop. Print result to debug it and see what you got. Commented May 18, 2021 at 21:49
  • Result has key items in the first loop at the top of the post. Commented May 18, 2021 at 21:52
  • If I print out result I see a bunch of output. There are keys and values there. Commented May 18, 2021 at 21:53
  • result has the same output from the 1st version and the 2nd version of the function. That's what's confusing me. Commented May 18, 2021 at 21:54
  • I would suggest a reproducible example, but it's telling you KeyError: 'items' so ... kind of self explanatory. Just because something else has items doesn't really matter. Maybe it's just one result that is lacking. You can default it to an empty list with result.get('items', []) or if you don't have a debugger you can catch KeyError and print that result. Commented May 18, 2021 at 21:56

2 Answers 2

1

As I mentioned in comments, earlier, you can just provide a default value, so you don't get an exception. And if the empty list is your default, you can still iterate over it. The trick here is "nothing happens" so this code gracefully skips over those errors:

for item in result.get('items', []):
    insert_coll_stuff(item)

docs: https://docs.python.org/3/library/stdtypes.html?highlight=get#dict.get
example: https://stackoverflow.com/a/11041421/1766544

Sign up to request clarification or add additional context in comments.

Comments

0

The problem was that there were no servers in some of the zones. So the items variable wouldn't exist in those zones.

Wrapping the code that creates the result variable in a try block solved the problem. This is the final version of the function:

def list_instances(compute,project_id,date):
    delete_from_collection(date)
    items = []
    zones = ['us-east1-b','us-east1-c','us-east1-d','us-east4-c','us-east4-b','us-east4-a','us-central1-c','us-central1-a','us-central1-f','us-central1-b','us-west1-b','us-west1-c','us-west1-a','europe-west4-a','europe-west4-b','europe-west4-c','europe-west1-b','europe-west1-d','europe-west1-c','europe-central2-a','europe-central2-b','europe-central2-c','europe-north1-a','europe-north1-b','europe-north1-c','europe-west6-a','europe-west6-b','europe-west6-c','northamerica-northeast1-a','northamerica-northeast1-b','northamerica-northeast1-c','us-west2-a','us-west2-b','us-west2-c','us-west3-a','us-west3-b','us-west3-c','us-west4-a','us-west4-b','us-west4-c']
    for zone in zones:
        try:
            result = compute.instances().list(project=project_id, zone=zone).execute()
            items = result['items']
        except Exception as e:
            pass
        if items:
            for item in items:
                instance_id = item['id']
                name = item['name']
                timestamp = item['creationTimestamp']
                private_ip = item['networkInterfaces'][0]['networkIP']
                instance_dict = {'Instance ID': instance_id, 'Name': name,'Network Interfaces': private_ip,'Timestamp': timestamp,}
                insert_coll(instance_dict,date)

2 Comments

Would recommend only catching the KeyError exception to be specific so you don't mask another problem. You can also do a continue in your exception logic
Ok that's good advice. I will do that. I appreciate the input.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.