How to configure LXD container devices using saltstack "lxd_container.present" function? - salt-stack

I have the following working Salt state file which: 1) downloads an image 2) creates a container from that image and 3) adds a nic afterwards.
These states work using the following setup:
salt-ssh: version 3004
Python: 3.9.7
LXC (snapd): version 5.0.0
PyLXD: version 2.3.0
Linux ubuntu: aarch64
---
# Create Penguin Container
#---
get_focal:
lxd_image.present:
- name: 'focal'
- source:
type: simplestreams
server: https://cloud-images.ubuntu.com/releases
name: '20.04'
create_penguin:
lxd_container.present:
- name: penguin
- profiles: ['default']
- source: 'focal'
- running: true
- devices:
### I want to create NIC here. ###
add_nic_card:
cmd.run:
- name: |
lxc config device add penguin eth0 nic nictype=bridged parent=br0
I need to combine states #2 and #3 so the nic is created simultaneously with the container. This should be possible according to the official documentation. However, I've not been able to get the syntax right, and the error codes aren't helpful.
I've tried numerous variations of the following:
variation 1
create_penguin:
lxd_container.present:
- name: penguin
- profiles: ['default']
- source: 'focal'
- running: true
- devices:
eth0: {
type: "nic",
nictype: "bridged",
parent: "br0" }
variation 2
create_penguin:
lxd_container.present:
- name: penguin
- profiles:
- default
- source: 'focal'
- running: true
- devices:
eth0:
type: nic
nictype: bridged
parent: br0
Variation 2 produces the following error:
----------
ID: create_penguin
Function: lxd_container.present
Name: penguin
Result: False
Comment: An exception occurred in this state: Traceback (most recent call last):
File "/var/tmp/.ubuntu_a31665_salt/pyall/salt/state.py", line 2179, in call
ret = self.states[cdata["full"]](
File "/var/tmp/.ubuntu_a31665_salt/pyall/salt/loader/lazy.py", line 149, in __call__
return self.loader.run(run_func, *args, **kwargs)
File "/var/tmp/.ubuntu_a31665_salt/pyall/salt/loader/lazy.py", line 1201, in run
return self._last_context.run(self._run_as, _func_or_method, *args, **kwargs)
File "/var/tmp/.ubuntu_a31665_salt/pyall/salt/loader/lazy.py", line 1216, in _run_as
return _func_or_method(*args, **kwargs)
File "/var/tmp/.ubuntu_a31665_salt/pyall/salt/loader/lazy.py", line 1249, in wrapper
return f(*args, **kwargs)
File "/var/tmp/.ubuntu_a31665_salt/pyall/salt/states/lxd_container.py", line 235, in present
__salt__["lxd.container_create"](
File "/var/tmp/.ubuntu_a31665_salt/pyall/salt/loader/lazy.py", line 149, in __call__
return self.loader.run(run_func, *args, **kwargs)
File "/var/tmp/.ubuntu_a31665_salt/pyall/salt/loader/lazy.py", line 1201, in run
return self._last_context.run(self._run_as, _func_or_method, *args, **kwargs)
File "/var/tmp/.ubuntu_a31665_salt/pyall/salt/loader/lazy.py", line 1216, in _run_as
return _func_or_method(*args, **kwargs)
File "/var/tmp/.ubuntu_a31665_salt/pyall/salt/modules/lxd.py", line 691, in container_create
container_device_add(name, dn, **dargs)
File "/var/tmp/.ubuntu_a31665_salt/pyall/salt/modules/lxd.py", line 1431, in container_device_add
return _set_property_dict_item(container, "devices", device_name, kwargs)
File "/var/tmp/.ubuntu_a31665_salt/pyall/salt/modules/lxd.py", line 3544, in _set_property_dict_item
raise SaltInvocationError("path must be given as parameter")
salt.exceptions.SaltInvocationError: path must be given as parameter
Started: 09:43:31.807609
Duration: 5147.141 ms
Changes:
----------

I took cue from the lxd-formula and as described in documentation, we need supply a dict to devices: in YAML format. So, the curly brackets { .. } are not required.
Following that, I found that the below syntax works fine:
create_penguin:
lxd_container.present:
- name: penguin
- profiles: ['default']
- source: 'focal'
- running: true
- devices:
eth0:
type: nic
nictype: bridged
parent: br0
# another network interface
eth1:
type: nic
nictype: bridged
parent: br0
For reference, my setup:
component
version
Salt
3003.3
Python
3.8.10
dist
ubuntu 20.04 focal
lxc (apt)
4.0.9
pylxd
2.3.1
And output from Saltstack (had removed eth1 from the state):
----------
ID: create_penguin
Function: lxd_container.present
Name: penguin
Result: True
Comment: 1 changes
Started: 12:38:58.041506
Duration: 27277.86 ms
Changes:
----------
devices:
----------
eth0:
Added device "eth0"

The following solution provides a satisfactory work around to the original post. Instead of adding a separate stanza, I've created a profile named "natted" and added it to the container. While it works with my version of Salt, it's a little less explicit/transparent than the "official" answer in documentation.
---
# Create & Start the Penguin Container
#---
penguin_create_start:
lxd_container.present:
- name: penguin
- source: 'focal'
- running: true
- profiles: ['default', 'natted']
...
The profile looks something like this:
---
# equivalent to --> 'lxc config device add penguin eth0 nic nictype=bridged parent=br0'
#---
config: {}
description: Adds eth1 to 'lxdbr0' bridge
devices:
eth1:
nictype: bridged
parent: lxdbr0
type: nic
name: natted

Related

Ansible Nested Loop for Cisco ACL

I'm creating a playbook for an ACL update, where the existing ACL needs to be updated, but before adding the new set of IP addresses to that ACL, I need to make sure that the ACL is present and that the IP hasn't already been configured.
Process:
Need to add the below IP addresses
ACL NAME: 11, 13, DATA_TEST, dummy
Check if the list of ACL are present
commands: "show access-lists {{item}}"
Check if ACL Exist
Q: Can't figure out how to access each item in the result of the first action to see if ACL has been configured. Ex. We can see from the output that dummy has no output, how can I exclude that and process if exist. (refer code below)
Check if IP addresses already added
Q: What is the best approach here? I'm thinking using when then comparing the ACL output from stdout vs the given variables content (ex. parents/lines)?
Add the set of IP addresses on target ACL
Q: What is the best approach here? Need to match the ACL name and configure using the variable.
If somebody is knowledgeable about Ansible, perhaps you could assist me in creating this project? I'm still doing some research, so any assistance you can give would be greatly appreciated. Thanks
My Code:
---
- name: Switch SVU
hosts: Switches
gather_facts: False
vars:
my_acl_list:
- 11
- 13
- DATA_TEST
- dummy
fail: "No such access-list {{item}}"
UP_ACL11:
parents:
- access-list 11 permit 192.168.1.4
- access-list 11 permit 192.168.1.5
UP_ACL13:
parents: access-list 13 permit 10.22.1.64 0.0.0.63
UP_ACLDATA:
lines:
- permit 172.11.1.64 0.0.0.63
- permit 172.12.2.64 0.0.0.63
parents: ip access-list standard DATA_TEST
tasks:
- name: Check if the ACL Name already exists.
ios_command:
commands: "show access-lists {{item}}"
register: acl_result
loop: "{{my_acl_list}}"
- debug: msg="{{acl_result}}"
- name: Check if ACL Exist
debug:
msg: "{{item.stdout}}"
when: item.stdout.exists
with_items: "{{acl_result.results}}"
loop_control:
label: "{{item.item}}"
# Pending - Need to know how to match if ACL name exist on stdout.
- name: Check if IP addresses already added
set_fact:
when:
# pending - ansible lookup?
# when var: UP_ACL11, UP_ACL13, UP_ACLDATA IPs are not in ACL then TRUE
- name: Add the set of IP addresses on target ACL
ios_config:
# pending - if doest exist on particular ACL name then configure using the var: UP_ACL11, UP_ACL13, UP_ACLDATA
Given the simplified data for testing
acl_result:
results:
- item: DATA_TEST
stdout:
- "Standard ... 10 permit ... 20 permit ..."
stdout_lines:
- - "Standard ..."
- "10 permit ..."
- "20 permit ..."
- item: dummy
stdout:
- ""
stdout_lines:
- - ""
Q: "Check if ACL Exists"
A: If ACL doesn't exist the attribute stdout is a list of empty strings. Test it
- name: Check if ACL Exists
debug:
msg: "{{ item.item }} exists: {{ item.stdout|map('length')|select()|length > 0 }}"
loop: "{{ acl_result.results }}"
loop_control:
label: "{{item.item}}"
gives
TASK [Check if ACL Exists] ********************************************
ok: [localhost] => (item=DATA_TEST) =>
msg: 'DATA_TEST exists: True'
ok: [localhost] => (item=dummy) =>
msg: 'dummy exists: False'
Notes:
In the filter select, "If no test is specified, each object will be evaluated as a boolean". The number 0 evaluates to false.
Example of a complete playbook for testing
- hosts: localhost
vars:
acl_result:
results:
- item: DATA_TEST
stdout:
- "Standard ... 10 permit ... 20 permit ..."
stdout_lines:
- - "Standard ..."
- "10 permit ..."
- "20 permit ..."
- item: dummy
stdout:
- ""
stdout_lines:
- - ""
tasks:
- name: Check if ACL Exists
debug:
msg: "{{ item.item }} exists: {{ item.stdout|map('length')|select()|length > 0 }}"
loop: "{{ acl_result.results }}"
loop_control:
label: "{{item.item}}"
The test can be simplified if you're sure stdout is a list with a single line only
msg: "{{ item.item }} exists: {{ item.stdout|first|length > 0 }}"

Autoscaling using Ceilometer/Aodh failed to trigger an alarm in the openstack rocky

Here is the document I refer to
1.sample_server.yaml
type: os.nova.server
version: 1.0
properties:
name: cirros_server
flavor: m1.small
image: b86fb462-c5c2-4a08-9fe4-c9f86d05763d
networks:
- network: external-net
2.Execute the following command line
# openstack cluster create --profile pserver --desired-capacity 2 mycluster
# openstack cluster receiver create --type webhook --cluster mycluster --action CLUSTER_SCALE_OUT --params count=2 r_01
# export ALRM_URL01='http://vip:8777/v1/webhooks/aac3433a-40de-4d7d-830c-e0035f2a4d13/trigger?V=1&count=2'
# aodh alarm create --type gnocchi_resources_threshold --aggregation-method mean --name cpu-high --metric cpu_util --threshold 70 --comparison-operator gt --granularity 300 --evaluation-periods 1 --alarm-action $ALRM_URL01 --repeat-actions False --query metadata.user_metadata.cluster_id=$MYCLUSTER_ID --resource-type instance --resource-id f7e0e8a6-51a3-422d-b631-7ddaf65b3dfb
3.log into each cluster nodes and run some CPU burning workloads there to drive the CPU utilization high
I added log output to /usr/lib/python2.7/site-packages/aodh/notifier/rest.py when trigger the alert request
class RestAlarmNotifier(notifier.AlarmNotifier):
def notify(self, action, alarm_id, alarm_name, severity, previous,
current, reason, reason_data, headers=None):
body = {'alarm_name': alarm_name, 'alarm_id': alarm_id,
'severity': severity, 'previous': previous,
'current': current, 'reason': reason,
'reason_data': reason_data}
headers['content-type'] = 'application/json'
kwargs = {'data': json.dumps(body),
'headers': headers}
max_retries = self.conf.rest_notifier_max_retries
session = requests.Session()
LOG.info('#########################')
LOG.info(session)
LOG.info(kwargs)
LOG.info(action.geturl())
LOG.info('#########################')
session.mount(action.geturl(),
requests.adapters.HTTPAdapter(max_retries=max_retries))
resp = session.post(action.geturl(), **kwargs)
LOG.info('$$$$$$$$$$$$$$$$$$$$$$$')
LOG.info(resp.content)
LOG.info('$$$$$$$$$$$$$$$$$$$$$$$')
Some error messages are output in the /var/log/aodh/notifier.log log, as follows:
enter image description here
The reason is the error caused by adding the body request parameter, the direct post request can be successful, for example, using curl request without the body parameter
curl -g -i -X POST 'http://vip:8777/v1/webhooks/34e91386-7176-4b30-bc17-5c3503712696/trigger?V=1'
Aodh related version packages are as follows:
python2-aodhclient-1.1.1-1.el7.noarch
openstack-aodh-api-7.0.0-1.el7.noarch
openstack-aodh-common-7.0.0-1.el7.noarch
openstack-aodh-listener-7.0.0-1.el7.noarch
python-aodh-7.0.0-1.el7.noarch
openstack-aodh-notifier-7.0.0-1.el7.noarch
openstack-aodh-evaluator-7.0.0-1.el7.noarch
openstack-aodh-expirer-7.0.0-1.el7.noarch
Can anyone point me in the right direction? Thanks.
The problem has been solved. Here is the document I refer to
Modify aodh rest.py(aodh/notifier/rest.py)
https://github.com/openstack/aodh/blob/master/aodh/notifier/rest.py#L79
Under the headers['content-type'] , add this line: headers['openstack-api-version'] = 'clustering 1.10'
Restart aodh service

Not able to execute lifecycle operation using script plugin

I'm trying to learn how to use script plugin. I'm following script plugin docs here but not able to make it work.
I've tried to use the plugin in two ways. The first, when cloudify.interface.lifecycle.start operation is mapped directly to a script:
tosca_definitions_version: cloudify_dsl_1_3
imports:
- 'http://www.getcloudify.org/spec/cloudify/4.5.5/types.yaml'
node_templates:
Import_Project:
type: cloudify.nodes.WebServer
capabilities:
scalable:
properties:
default_instances: 1
interfaces:
cloudify.interfaces.lifecycle:
start:
implementation: scripts/create_project.sh
inputs: {}
The second with a direct mapping:
tosca_definitions_version: cloudify_dsl_1_3
imports:
- 'http://www.getcloudify.org/spec/cloudify/4.5.5/types.yaml'
node_templates:
Import_Project:
type: cloudify.nodes.WebServer
capabilities:
scalable:
properties:
default_instances: 1
interfaces:
cloudify.interfaces.lifecycle:
start:
implementation: script.script_runner.tasks.run
inputs:
script_path: scripts/create_project.sh
I've created a directory named scripts and placed the below create_project.sh script in this directory:
#! /bin/bash -e
ctx logger info "Hello to this world"
hostname
I'm getting errors while validating the blueprint.
Error when operation is mapped directly to a script:
[2019-04-13 13:29:40.594] [DEBUG] DslParserExecClient - got output from dsl parser Could not extract plugin from operation mapping 'scripts/create_project.sh', which is declared for operation 'start'. In interface 'cloudify.interfaces.lifecycle' in node 'Import_Project' of type 'cloudify.nodes.WebServer'
in: /opt/cloudify-composer/backend/dev/workspace/2/tmp-27O0e1t813N6as
in line: 3, column: 2
path: node_templates.Import_Project
value: {'interfaces': {'cloudify.interfaces.lifecycle': {'start': {'implementation': 'scripts/create_project.sh', 'inputs': {}}}}, 'type': 'cloudify.nodes.WebServer', 'capabilities': {'scalable': {'properties': {'default_instances': 1}}}}
Error when using a direct mapping:
[2019-04-13 13:25:21.015] [DEBUG] DslParserExecClient - got output from dsl parser node 'Import_Project' has no relationship which makes it contained within a host and it has a plugin 'script' with 'host_agent' as an executor. These types of plugins must be installed on a host
in: /opt/cloudify-composer/backend/dev/workspace/2/tmp-279QCz2CV3Y81L
in line: 2, column: 0
path: node_templates
value: {'Import_Project': {'interfaces': {'cloudify.interfaces.lifecycle': {'start': {'implementation': 'script.script_runner.tasks.run', 'inputs': {'script_path': 'scripts/create_project.sh'}}}}, 'type': 'cloudify.nodes.WebServer', 'capabilities': {'scalable': {'properties': {'default_instances': 1}}}}}
What is missing to make this work?
I also found the Cloudify Script Plugin examples from their documentation do not work: https://docs.cloudify.co/4.6/working_with/official_plugins/configuration/script/
However, I found I can make the examples work by adding an executor line in parallel with the implementation line to override the host_agent executor as follows:
tosca_definitions_version: cloudify_dsl_1_3
imports:
- 'http://www.getcloudify.org/spec/cloudify/4.5.5/types.yaml'
node_templates:
Import_Project:
type: cloudify.nodes.WebServer
capabilities:
scalable:
properties:
default_instances: 1
interfaces:
cloudify.interfaces.lifecycle:
start:
implementation: scripts/create_project.sh
executor: central_deployment_agent
inputs: {}

salt cloud launching EC2 servers into wrong subnet

This is really weird. I'm launching some windows 2012 servers into EC2 using salt-cloud. And tho I'm using this profile:
ec2_private_win_app1:
provider: company-nonpod-us-east-1
image: ami-xxxxxx
size: c4.large
network_interfaces:
- DeviceIndex: 0
PrivateIpAddresses:
- Primary: True
#auto assign public ip (not EIP)
AssociatePublicIpAddress: False
SubnetId: subnet-A
SecurityGroupId: sg-xxxxxx
#block_device_mappings:
# - DeviceName: /dev/sda1
# Ebs.VolumeSize: 120
# Ebs.VolumeType: gp2
# - DeviceName: /dev/sdf
# Ebs.VolumeSize: 100
# Ebs.VolumeType: gp2
tag: {'Engagement': '2112254190125', 'Owner': 'Tim', 'Name': 'production'}
And giving this command:
salt-cloud -p ec2_private_win_app1 USAB00005
The resulting server ends up in this subnet in AWS:
Subnet ID: subnet-B
I'm using salt-cloud version: salt-cloud 2016.9.0-410-gdedfd82
On a server running: CentOS Linux release 7.2.1511
Just what in the hell is going on?
It was a yaml formatting problem. I ran the yaml through an online yaml parser, and was able to correct the issue:
ec2_private_win_app1:
provider: company-nonpod-us-east-1
image: ami-xxxxx
size: c4.large
ssh_username: root
network_interfaces:
- DeviceIndex: 0
SubnetId: subnet-xxxxxx
PrivateIpAddresses:
- Primary: True
#auto assign public ip (not EIP)
AssociatePublicIpAddress: False
SecurityGroupId:
- sg-xxxxxx
Basically, I had to group the Subnet ID in the network_interfaces section in order for the servers to appear in the correct subnet.

Wordpress on bluemix cloudfoundry crashing

I followed the steps on the blog to get wordpress going
https://blog.pivotal.io/pivotal-cloud-foundry/products/getting-started-with-wordpress-on-cloud-foundry
when I do a cf push it keeps crashing with the following lines in the error
2016-05-14T15:41:44.22-0700 [App/0] OUT total size is 2,574,495 speedup is 0.99
2016-05-14T15:41:44.24-0700 [App/0] ERR fusermount: entry for /home/vcap/app/htdocs/wp-content not found in /etc/mtab
2016-05-14T15:41:44.46-0700 [App/0] OUT 22:41:44 sshfs | fuse: mountpoint is not empty
2016-05-14T15:41:44.46-0700 [App/0] OUT 22:41:44 sshfs | fuse: if you are sure this is safe, use the 'nonempty' mount option
2016-05-14T15:41:44.64-0700 [DEA/86] ERR Instance (index 0) failed to start accepting connections
2016-05-14T15:41:44.68-0700 [API/1] OUT App instance exited with guid cf2ea899-3599-429d-a39d-97d0e99280e4 payload: {"cc_partition"=>"default", "droplet"=>"cf2ea899-3599-429d-a39d-97d0e99280e4", "version"=>"c94b7baf-4da4-44b5-9565-dc6945d4b3ce", "instance"=>"c4f512149613477baeb2988b50f472f2", "index"=>0, "reason"=>"CRASHED", "exit_status"=>1, "exit_description"=>"failed to accept connections within health check timeout", "crash_timestamp"=>1463265704}
2016-05-14T15:41:44.68-0700 [API/1] OUT App instance exited with guid cf2ea899-3599-429d-a39d-97d0e99280e4 payload: {"cc_partition"=>"default", "droplet"=>"cf2ea899-3599-429d-a39d-97d0e99280e4", "version"=>"c94b7baf-4da4-44b5-9565-dc6945d4b3ce", "instance"=>"c4f512149613477baeb2988b50f472f2", "index"=>0, "reason"=>"CRASHED", "exit_status"=>1, "exit_description"=>"failed to accept connections within health check timeout", "crash_timestamp"=>1463265704}
^[[A
my manifest file:
cf-ex-wordpress$ cat manifest.yml
---
applications:
- name: myapp
memory: 128M
path: .
buildpack: https://github.com/cloudfoundry/php-buildpack
host: near
services:
- mysql-db
env:
SSH_HOST: user#abc.com
SSH_PATH: /home/user
SSH_KEY_NAME: sshfs_rsa
SSH_OPTS: '["cache=yes", "kernel_cache", "compression=no", "large_read"]'
vagrant#vagrant:~/Documents/shared/cf-ex-wordpress$
Please check your SSH mount, more details at https://github.com/dmikusa-pivotal/cf-ex-wordpress/issues

Resources