I'm using boto3 to verify if a cloudformation stack already exists. The code for this is:
import boto3
import botocore
conn = boto3.client('cloudformation')
list_stack = conn.list_stacks()['StackSummaries']
for stack in list_stack:
if stack['StackStatus'] == 'DELETE_COMPLETE':
continue
if stack['StackName'] == stack_name:
return True
return False
The conn.list_stack() returns a dict.
This code works properly.
What I don't understand is why I need to put ['StackSummaries'] after the list_stack()? I've never seen this kind of code where you put the key of a dictionary after the function. I just don get it the syntax.
And if I don use this way a get this error message:
TypeError: string indices must be integers
So I need to use with the suntax that I don understand. Can someone explain how this works?
The output for list_stacks() is:
{
'StackSummaries': [
{
'StackId': 'string',
'StackName': 'string',
'TemplateDescription': 'string',
'CreationTime': datetime(2015, 1, 1),
'LastUpdatedTime': datetime(2015, 1, 1),
'DeletionTime': datetime(2015, 1, 1),
'StackStatus': 'CREATE_IN_PROGRESS'|'CREATE_FAILED'|'CREATE_COMPLETE'|'ROLLBACK_IN_PROGRESS'|'ROLLBACK_FAILED'|'ROLLBACK_COMPLETE'|'DELETE_IN_PROGRESS'|'DELETE_FAILED'|'DELETE_COMPLETE'|'UPDATE_IN_PROGRESS'|'UPDATE_COMPLETE_CLEANUP_IN_PROGRESS'|'UPDATE_COMPLETE'|'UPDATE_ROLLBACK_IN_PROGRESS'|'UPDATE_ROLLBACK_FAILED'|'UPDATE_ROLLBACK_COMPLETE_CLEANUP_IN_PROGRESS'|'UPDATE_ROLLBACK_COMPLETE'|'REVIEW_IN_PROGRESS'|'IMPORT_IN_PROGRESS'|'IMPORT_COMPLETE'|'IMPORT_ROLLBACK_IN_PROGRESS'|'IMPORT_ROLLBACK_FAILED'|'IMPORT_ROLLBACK_COMPLETE',
'StackStatusReason': 'string',
'ParentId': 'string',
'RootId': 'string',
'DriftInformation': {
'StackDriftStatus': 'DRIFTED'|'IN_SYNC'|'UNKNOWN'|'NOT_CHECKED',
'LastCheckTimestamp': datetime(2015, 1, 1)
}
},
],
'NextToken': 'string'
}
The outer-most dictionary element is StackSummaries, which contains a list.
So, it is returning the list.
Instead of:
list_stack = conn.list_stacks()['StackSummaries']
for stack in list_stack:
they could have used:
stacks = conn.list_stacks()
for stack in stacks['StackSummaries']:
The good thing is that they called it list_stacks, which suggests that the contents is a list.
Related
I have been working with BOTO 3 to describe all load balancers available in the account. I used the following snippet of code:
'elbv2=boto3.client('elbv2',aws_access_key_id=access_key_id,aws_secret_access_key=secret_key,region_name=region)
response=elbv2.describe_load_balancers()
print(response)
The response here stores the dict with all the information, like so:
{
'LoadBalancers': [{
'LoadBalancerArn': 'arn:aws:elasticloadbalancing:ap-south-1:407203256002:loadbalancer/net/aws-lb-02/9d4b15bfd6f579d3',
'DNSName': 'aws-lb-02-9d4b15bfd6f579d3.elb.ap-south-1.amazonaws.com',
'CanonicalHostedZoneId': 'ZVDDRBQ08TROA',
'CreatedTime': datetime.datetime(2021, 3, 31, 11, 45, 6, 729000, tzinfo = tzutc()),
'LoadBalancerName': 'aws-lb-02',
'Scheme': 'internet-facing',
'VpcId': 'vpc-0be01860',
'State': {
'Code': 'active'
},
'Type': 'network',
'AvailabilityZones': [{
'ZoneName': 'ap-south-1a',
'SubnetId': 'subnet-ed5fb986',
'LoadBalancerAddresses': []
}, {
'ZoneName': 'ap-south-1b',
'SubnetId': 'subnet-89d285c5',
'LoadBalancerAddresses': []
}]]}"'
I want to access LoadBalancerAddress , which I tried like this:
LoadBalancers=response['LoadBalancers']
for i in LoadBalancers:
AvailabilityZones=i['AvailabilityZones']
for j in AvailabilityZones:
LoadBalancerAddresses=i['LoadBalancerAddresses']
However, it throws an error saying that there is a keyword error for LoadBalancerAddresses, which I fail to understand.
Please help in how should I access the variable.
you can use nested list comprehension here, like that:
addresses = [x['LoadBalancerAddresses'] for res in aaa['LoadBalancers'] for x in res['AvailabilityZones']]
or with ordinar nested lists:
addresses = []
for bal in aaa['LoadBalancers']:
for zones in bal['AvailabilityZones']:
addresses += zones['LoadBalancerAddresses']
You mistyped j['LoadBalancerAddresses'] as i['LoadBalancerAddresses']. Since there are no keys named LoadBalancerAddresses directly under response['LoadBalancers'], your program throws a KeyError.
The fixed version:
LoadBalancers=response['LoadBalancers']
for i in LoadBalancers:
AvailabilityZones=i['AvailabilityZones']
for j in AvailabilityZones:
LoadBalancerAddresses=j['LoadBalancerAddresses']
As a safety option, it's good practice to check if the key exists before you access it, such as:
for j in AvailabilityZones:
if "LoadBalancerAddresses" in j:
LoadBalancerAddresses=j['LoadBalancerAddresses']
else:
print("The key does not exist")
I am following the tutorial in Pymongo documentation
import pymongo
from pymongo import MongoClient
import datetime
client = MongoClient("mongodb://localhost:27017/")
test_db = client.test_db #This creates a database
posts = test_db.posts #This creates a collection of documents
post = {"author": "Doug",
"text": "My first blog post!",
"tags": ["mongodb", "python"],
"date": datetime.datetime.utcnow()}
post_id = posts.insert_one(post).inserted_id
The code works in both cases: press run in IDE or run line by line in python console. However, whenever I run it in python console, it gives me the
pymongo.errors.DuplicateKeyError: E11000 duplicate key error collection: text_database.another_collection index: _id_ dup key: { _id: ObjectId('5f505d1e233d210283dd4632') }, full error: {'index': 0, 'code': 11000, 'keyPattern': {'_id': 1}, 'keyValue': {'_id': ObjectId('5f505d1e233d210283dd4632')}, 'errmsg': "E11000 duplicate key error collection: text_database.another_collection index: _id_ dup key: { _id: ObjectId('5f505d1e233d210283dd4632') }"}
error. It is normal in creating a new empty collection and inserting the document except the duplicate error. May I ask why?
After insert_one() your post dictionary will look like this
>>>print(post)
{'_id': ObjectId('5f506285b54093f4b9202abe'),
'author': 'Doug',
'date': datetime.datetime(2020, 9, 3, 3, 27, 1, 729287),
'tags': ['mongodb', 'python'],
'text': 'My first blog post!'}
Now you trying to insert again with same post it will throw an error because now it has an _id field.
If you don't want to update the dictionary after insert. You can insert using the copy of dictionary. you can create copy of dictionary using dict(post) or post.copy()
Now you can change your insert as below
post_id = posts.insert_one(dict(post)).inserted_id
or
post_id = posts.insert_one(post.copy()).inserted_id
I am trying to get the list of CF Stacks with preferred items "Stack Name", "StackStatus" and "DriftInformation".
I tried the following and it throws an error
"TypeError: list indices must be integers or slices, not str"
import boto3
client = boto3.client('cloudformation')
response = client.list_stacks(
)
list = response['StackSummaries']['StackName']
print(list)
Here is the response syntax.
'StackSummaries': [
{
'StackId': 'string',
'StackName': 'string',
'TemplateDescription': 'string',
'CreationTime': datetime(2015, 1, 1),
'LastUpdatedTime': datetime(2015, 1, 1),
'DeletionTime': datetime(2015, 1, 1),
'StackStatus': 'CREATE_IN_PROGRESS'|'CREATE_FAILED'|'CREATE_COMPLETE'|'ROLLBACK_IN_PROGRESS'|'ROLLBACK_FAILED'|'ROLLBACK_COMPLETE'|'DELETE_IN_PROGRESS'|'DELETE_FAILED'|'DELETE_COMPLETE'|'UPDATE_IN_PROGRESS'|'UPDATE_COMPLETE_CLEANUP_IN_PROGRESS'|'UPDATE_COMPLETE'|'UPDATE_ROLLBACK_IN_PROGRESS'|'UPDATE_ROLLBACK_FAILED'|'UPDATE_ROLLBACK_COMPLETE_CLEANUP_IN_PROGRESS'|'UPDATE_ROLLBACK_COMPLETE'|'REVIEW_IN_PROGRESS',
'StackStatusReason': 'string',
'ParentId': 'string',
'RootId': 'string'
},
],
'NextToken': 'string'
}
I just want to list the stacks with below items.
"Stack Name", "StackStatus" and "DriftInformation". Please guide on this.
Thanks!
Update:
import boto3
client = boto3.client('cloudformation')
response = client.list_stacks(
)
for stack in response['StackSummaries']:
stack_summary = stack['StackName']
print(stack_summary)
import boto3
client = boto3.client('cloudformation')
response = client.list_stacks()
for stack in response['StackSummaries']:
stack_summary = stack['StackName']
stack_status = stack['StackStatus']
stack_drift_status = stack['DriftInformation']['StackDriftStatus']
stack_drift_timestamp = stack['DriftInformation']['LastCheckTimestamp']
It should be the following:
list = response['StackSummaries'][0]['StackName']
StackSummaries is an array.
For full records, how to do it in the loop:
for stack in response['StackSummaries']:
stack_summary = stack['StackName']
p.s.
Don't use list as a variable name.
Returns the description for the specified stack; if no stack name was specified, then it returns the description(use "describe_stacks").
import boto3
client = boto3.client('cloudformation')
response = client.describe_stacks(
StackName='string',
StackStatus= 'string',
DriftInformation': {
'StackDriftStatus': 'DRIFTED'|'IN_SYNC'|'UNKNOWN'|'NOT_CHECKED',
'LastCheckTimestamp': datetime(2015, 1, 1)
}
)
I have this simple Python script, it is supposed to be part of something bigger I just cannot figure out how to work with jmespath
#!/usr/bin/env python
import jmespath
if __name__ == '__main__':
# input json
text = \
{
'topology': [
{
'node': [
{
'topology-stats:session-state': {
'synchronized': True,
'local-pref': {
'session-id': 0,
'deadtimer': 120,
'ip-address': '10.30.170.187',
'keepalive': 30
},
'messages': {
'stateful-stats:sent-upd-msg-count': 1,
'last-sent-msg-timestamp': 1513334157,
'stateful-stats:last-received-rpt-msg-timestamp': 1513334157,
'unknown-msg-received': 0,
'stateful-stats:received-rpt-msg-count': 3,
'reply-time': {
'max-time': 77,
'average-time': 77,
'min-time': 77
},
'stateful-stats:sent-init-msg-count': 0,
'sent-msg-count': 1,
'received-msg-count': 3
},
'session-duration': '0:00:00:12'
},
'node-id': '10.30.170.117'
}
],
'topology-id': 'asdf-topology'
}
]
}
exp = jmespath.compile('''topology[*].node[?'topology-stats:session-state' != 'session-duration'][]''')
result = exp.search(text)
print result
What I want is to basically remove lines with keys which have unpredictable values ( in perfect world I would switch the value for something generic ) - like: last-sent-msg-timestamp, session-duration, stateful-stats:last-received-rpt-msg-timestamp. And perfectly I would want to keep everything else, although I can live with loosing topology and node tags.
The thing is I can use only one jmespath.search and I can do only one jmespath expression. Also I cannot use anything else from Python - this script is just example.
Is this possible with jmespath? It is currently my best option due to limitations of project.
Removing fields with Jmespath is not possible currently. There is a pending feature request:
Ability to set and delete based on a jmespath #121
https://github.com/jmespath/jmespath.py/issues/121
I am using jq to do that:
jq 'del(.foo)'
Input {"foo": 42, "bar": 9001, "baz": 42}
Output {"bar": 9001, "baz": 42}
I have written code to migrate data from sql server 2008 to PostGreSQL using OpenERPLib in Python for OpenERP. I want to set the value of "categ_id" column of type "Many2one" of "crm.opportunity2phonecall" object. Here below is my existing code.
scheduleCall = {
'name': 'test',
'action': ['schedule'],
'phone': "123456",
'user_id': 1,
"categ_id": 10,
'note': mail['body']
}
SCHEDULECALL_MODEL.create(scheduleCall)
SCHEDULECALL_MODEL = OECONN.get_model("crm.opportunity2phonecall")
In the above code i have set the hard-coded value "10" for "categ_id" field as per my requirement. When i execute above code, it gives me an error -
TypeError: unhashable type: 'list'
Try assigning a list instead of an integer as follows:
categ_id: [10]
anyway, as Atul said in his comment, update OpenERP with xmlrpc, it is safe and stable, and suppports different versions of OpenERP
Okay, I got the solution.
What i had done is - define one method in python which returns categ_id and set its value in "scheduleCall" dict and surprisingly its work. Here is my code.
scheduleCall = {
'name': 'test',
'action': ['schedule'],
'phone': "123456",
'user_id': 1,
"categ_id": get_categid_by_name('Outbound'),
'note': mail['body']
}
SCHEDULECALL_MODEL.create(scheduleCall)
SCHEDULECALL_MODEL = OECONN.get_model("crm.opportunity2phonecall")
And here is the method that i had define.
def get_categid_by_name(name):
"""return category id"""
categ_id = False
ids = CATEG_MODEL.search([('name', '=', name)])
categ_id = ids[0]
return categ_id
CATEG_MODEL = OECONN.get_model("crm.case.categ")
Hope it'll help to others.