Friday, 19 November 2021

Making a Raspberrypi Stop Motion Video

This guide explains how to make a stop motion video using a raspberrypi with the camera module. As part of this exercise I also wanted to transfer the file over to another remote PC in an automated fashion.
The reason I wanted to do this was the using video made the filesize umanageable. With stop motion you can alter the interval, duration of process etc.

High level steps:

1. pi1 takes a picture every 10 seconds 
2. pi1 copies the picture to pi2
3. pi1 deletes the local copy and takes another picture and repeats the process.
4. On pi2 there is a scheduled cronjob that creates a video from the still images and then deletes the images files.

On pi1:

Create a folder to store our images:
mkdir /home/pi/camera

Create a script to capture the images:

sudo nano /home/pi/camera.sh

Add the following to the camera.sh shell script. The script itself runs through a for loop, you can see in this example it runs through 3600 iterations.  Within the loop the script takes a picture and outputs it to a file with a filename called picture-i (where i is the number where we are in the loop). The script then pauses for 10 seconds. This means that this script would take roughly 10 hours to work through the loop until it stops - you can obviously modify the values to suit your needs.  The script then writes the file to pi2 using scp - you need to have already setup ssh login without password for this to work. The script then deletes the local file and returns to the start of the loop. The deletion of the local file is purely to save space - it is not compulsory:

#!/bin/bash

#DATE=$(date +"%Y-%m-%d_%H%M%S")

for ((i=1; i<=3600; i++))

do

        DATE=$(date +"%Y-%m-%d_%H-%M-%S")

        echo "*** Taking Picture $i ***" 

        raspistill -o /home/pi/camera/picture-$i.jpg

        sleep 10


        echo "*** Writing file $i to remote server ***"

        scp /home/pi/camera/*.jpg pi@pi2:/home/pi/camera

        rm /home/pi/camera/*.jpg

done

Press CTRL + X, followed by Y to close the file and save it:

Make the script executable:
sudo chmod +x camera.sh

Now we move to pi2

Create a folder to store our images:
mkdir /home/pi/camera

Back on pi1 if we execute the script we should the .jpg files appearing in our folder on pi2.
cd /home/pi/camera
./camera.sh

We now need a method to create the video from the still images and delete the images to save space.

Create a script to make the video:
sudo nano /home/pi/make-video.sh

Add the following:
!/bin/bash

#DATE=$(date +"%Y-%m-%d_%H%M%S")

ffmpeg -framerate 25 -i /home/pi/camera/picture-%d.jpg /home/pi/Video-$(date +%d-%m-%Y-%H-%M).mp4

rm /home/pi/camera/*.jpg


Press CTRL + X, followed by Y to close the file and save it:

Make the script executable:
sudo chmod +x make-video.sh

Executing this script uses ffmpeg to create a video file at 25fps using the current date and time in the filename. It then removes all .jpg files from the folder.

Finally we create a cronjob to create the video periodically:

crontab -e

Add the following:
0 8 * * * sh /home/pi/camera/make-video.sh

This will run the script at 8am every day.

You can also create a cronjob on p1 to automate the other script.

crontab -e

Add the following:

15 8 * * * sh /home/pi/camera/camera.sh

Thursday, 18 February 2021

BIG-IP

! Load factory default cofig
tmsh load /sys config default

! Run management interface setup utility
config# config



Tuesday, 2 February 2021

F5 BIGIP Ansible

See here for more info:

https://github.com/F5Networks/f5-ansible/blob/devel/examples/0000-getting-started/playbook.yaml

Directory structure looks like this:

├── inventory

│   └── hosts

└── playbook.yaml

"hosts" file contains a single entry "localhost" (the F5 IP address is defined within the script).


<save the below to playbook.yaml>

 ---


- name: Create a VIP, pool and pool members

  hosts: all

  connection: local


  vars:

    provider:

      password: admin

      server: 192.168.1.245

      user: admin

      validate_certs: no

      server_port: 443


  tasks:

    - name: Create a pool

      bigip_pool:

        provider: "{{ provider }}"

        lb_method: ratio-member

        name: web

        slow_ramp_time: 120

      delegate_to: localhost


    - name: Add members to pool

      bigip_pool_member:

        provider: "{{ provider }}"

        description: "webserver {{ item.name }}"

        host: "{{ item.host }}"

        name: "{{ item.name }}"

        pool: web

        port: 80

      with_items:

        - host: 10.10.10.10

          name: web01

        - host: 10.10.10.20

          name: web02

      delegate_to: localhost


    - name: Create a VIP

      bigip_virtual_server:

        provider: "{{ provider }}"

        description: foo-vip

        destination: 172.16.10.108

        name: vip-1

        pool: web

        port: 80

        snat: Automap

        profiles:

          - http

          - clientssl

      delegate_to: localhost


Friday, 9 November 2018

Script to create tenant / app profile / EPG

#   Note that this script expects HTTP port 80 on the APIC, which is off by default.
# To enable HTTP in the APIC, navigate to FABRIC, FABRIC POLICIES Pod Policies     Policies   Management Acces    default  then enable HTTP
import requests
import json

def get_cookies(apic):
    username = 'admin'
    password = 'ciscoapic'
    url = apic + '/api/aaaLogin.json'
    auth = dict(aaaUser=dict(attributes=dict(name=username, pwd=password)))
    authenticate = requests.post(url, data=json.dumps(auth), verify=False)
    return authenticate.cookies

def add_tenant(apic,cookies):
    jsondata = {"fvTenant":{"attributes":{"dn":"uni/tn-acme","name":"acme","rn":"tn-acme","status":"created"},"children":[]}}
    result = requests.post('{0}://{1}/api/node/mo/uni/tn-acme.json'.format(protocol,host), cookies=cookies, data=json.dumps(jsondata), verify=False)
    print result.status_code
    print result.text

def get_tenants(apic,cookies):
    uri = '/api/class/fvTenant.json'
    url = apic + uri
    req = requests.get(url, cookies=cookies, verify=False)
    response = req.text
    return response

def add_application_profile(apic,cookies):
    jsondata = {"fvAp":{"attributes":{"dn":"uni/tn-acme/ap-Accounting","name":"Accounting","rn":"ap-Accounting","status":"created"},"children":[]}}
    result = requests.post("{0}://{1}/api/node/mo/uni/tn-acme/ap-Accounting.json".format(protocol, host), cookies=cookies, data=json.dumps(jsondata), verify=False)
    print result.status_code
    print result.text

def add_EPG1(apic,cookies):
    jsondata = {"fvAEPg":{"attributes":{"dn":"uni/tn-acme/ap-Accounting/epg-Payroll","name":"Payroll","rn":"epg-Payroll","status":"created"},"children":[{"fvCrtrn":{"attributes":{"dn":"uni/tn-acme/ap-Accounting/epg-Payroll/crtrn","name":"default","rn":"crtrn","status":"created,modified"},"children":[]}}]}}
    result = requests.post("{0}://{1}/api/node/mo/uni/tn-acme/ap-Accounting/epg-Payroll.json".format(protocol, host), cookies=cookies, data=json.dumps(jsondata), verify=False)
    print result.status_code
    print result.text

def add_EPG2(apic,cookies):
    jsondata = {"fvAEPg":{"attributes":{"dn":"uni/tn-acme/ap-Accounting/epg-Bills","name":"Bills","rn":"epg-Bills","status":"created"},"children":[{"fvCrtrn":{"attributes":{"dn":"uni/tn-acme/ap-Accounting/epg-Bills/crtrn","name":"default","rn":"crtrn","status":"created,modified"},"children":[]}}]}}
    result = requests.post("{0}://{1}/api/node/mo/uni/tn-acme/ap-Accounting/epg-Bills.json".format(protocol, host), cookies=cookies, data=json.dumps(jsondata), verify=False)
    print result.status_code
    print result.text

if __name__ == "__main__":
    protocol = 'http'
    host = '192.168.10.1'
    apic = '{0}://{1}'.format(protocol, host)
    cookies = get_cookies(apic)
    add_tenant(apic,cookies)
    add_application_profile(apic,cookies)
    add_EPG1(apic,cookies)
    add_EPG2(apic,cookies)
    rsp = get_tenants(apic,cookies)

rsp_dict = json.loads(rsp)
tenants = rsp_dict['imdata']

for tenant in tenants:
    print tenant['fvTenant']['attributes']['name']

Monday, 5 November 2018

Python

Integers and Floats

Integer = number
int (pi) ==3
Float = decimal number
float(answer) == 42.0

Strings

String = text

"Hello World"

"hello" .capitalize() == "Hello"

"hello" .replace("e" ,"a" ) == "hallo"
"hello" .isalpha() == True
"123" .isdigit() == True 
"some,csv,values" .split(",") == ["some", "csv", "values"]


name = "Martin"machine = "Hal"print ("Nice to meet you {0}. I am {1}".format(name,machine))

Boolean and None

python_course = True
int (python_course) == 1

If Statements

number = 5
if number == 5:
      print ("Number is 5")
else:
      print ("Number is NOT 5")

Lists (mutable, ordered)

student_names = ["John", "Paul", "George","Ringo"]
student_names[0] == "John"
! List values start at 1
student_names[-1] == "Ringo"
! Minus sign reads values from the right of the list
len(student_names) == 4
del student_names[2]
! Remove George from list

Dictionaries (mutable, associative array)

Device = {"hostname":"router1","OS":"v15.5,"location":"London")

Tuple (sequence of immutable objects)

Credentials = ("hostname","username","password")

Sets (unordered collection of unique and immutable objects) 

Loops

for name in student_names
     print ("Student name is {0}" .format(name))

For Loop

student_names = ["John", "Paul", "George","Ringo"]
for name in student_names:
  if name == "John":
  print("Found him! " + name)
  break 



Challenges:

Challenge 1:

#!/usr/bin/env python2.7

def devices():
 routers = ["router1","router2","router3"]
 print routers

def security():
 credentials = {"router1":"passw0rd1","router2":"passw0rd1","router3":"passw0rd1"}
 print credentials

def combined():
 devices()
 security()

if __name__ == "__main__":
 print "The routers are:"
 devices()
 
 print "The credentials are:"
 security()

 print "All data is:"
 combined()

Wednesday, 27 June 2018

ACI Deep Dive


  • TEP address pool should not overlap with internal address space
  • /16 address space is default for TEP pool

Switch discovery
  • LLDP between switch and APIC
  • DHCP request from switch for lo0
  • ISIS between leaf and spine
  • IFM = inter fabric messaging (secured with x.509 certificates) 
  • VXLAN tunnels built for connectivity to all other leaf / spine switches


Useful Commands

! Show switches in fabric
#acidiag fnvread
#acidiag verifyapic
#acidiag avread

! NXOS like interface
#vsh
#vsh_lc
#show cli list

! overlay-1 is the "underlay"

#show ip interface vrf overlay-1
#show ip route vrf overlay-1

https://<apic-ip>/visore

#moquery

! query faults - uses http port 777
#icurl


#show system internal epm endpoint mac aaaa.bbbb.cccc

! Leaf command to ping (vrf aware unlike native linux)
#iping

! TCPDUMP can be used for control plane traffic only
#tcpdump -i eth0 

ELAM - data plane traffic capture

! See denied packet between EPGs
#show logging ip access-list internal packet-log deny
#show logging ip access-list cache deny

vzAny - contract for an EPG to consume everything in a VRF

! Like BGP debug
#show bgp event-history events


Friday, 15 February 2013

My Five Most Annoying IOS Features

That is Cisco IOS by the way - if you think this is anything to do with iphones I suggest you run along because Cisco have been calling it IOS since Apple was just a sapling.
I like Cisco IOS but there are just a few annoying "features" than continually annoy / baffle me. There are probably legitimate reasons for their existence but to me they just seem like little flaws that could be easily ironed out but we have all just learned to live with.

  1. Context sensitive help and autocomplete do not work in configuration mode
    Being lazy by nature I always type as little as I need to which is why autocomplete is great. I can type "sh int" and I get a list of the interfaces on the router. If I am not sure if that is the command I want I can hit tab and it will show me the autocomplete entry for what I have typed so far:

    R1#sh int <Press TAB>
    R1#sh interfaces


    If there are multiple autocomplete entries for what I have typed so far tab does nothing but a question mark will show me what my options are:

    R1#sh in <Press TAB>
    R1#sh in <Press TAB again, slightly harder while frowning>
    R1#sh in?
    interfaces  inventory

    That's all great. Now let's go into configuration mode. We can use the "do" command to enter exec level commands when in configuration mode:

    R1#conf t
    Enter configuration commands, one per line.  End with CNTL/Z.
    R1(config)#do sh int

    This works, but only because "show interfaces" is the only autocomplete option for "sh int". Forgotten the command? Hard luck:

    R1(config)#do sh in <Press TAB>
    R1(config)#do sh in? <Press ENTER>
    LINE    <cr>
    <Bang head on keyboard - what the hell does LINE mean???>

    So you are stuck in a weird situation where, if you know the full command or the only viable autocomplete option then you can enter it, otherwise you get no help. This seems very obtuse to me - it is like IOS knows what you want but will only help you when it feels like it. Would it really be so hard to fix this?
  2. You must write the full interface name in the extended ping
    Not a huge labour but a bit irksome. When you run an extended ping and specify the source interface in the shortened form IOS gets all sniffy and makes you write the whole thing. It takes me about 4 hours for me to type "gigabitethernet"

    Protocol [ip]:
    Target IP address: 1.2.3.4
    Repeat count [5]:
    Datagram size [100]:
    Timeout in seconds [2]:
    Extended commands [n]: y
    Source address or interface: fa0/0
    Translating "fa0/0
    % Invalid source. Must use IP address or full interface name without spaces (e.g. Serial0/1)Source address or interface: fastethernet0/0
  3. Sometimes you have to add a parameter when it should really be done automatically
    Some commands only take one keyword as a parameter but IOS forces you to put it there even though there are no other options than having it - so why not put it there automatically?

    An example:

    R1(config-if)#ip nbar ?
      protocol-discovery  Enable NBAR protocol discovery

    R1(config-if)#ip nbar
    % Incomplete command.

    R1(config-if)#ip nbar ?
      protocol-discovery  Enable NBAR protocol discovery

    R1(config-if)#ip nbar protocol-discovery ?
      <cr>

    Here we have the command "ip nbar" which only takes the parameter "protocol-discovery". You can't leave it off and it is the only parameter it can take. So why not just fill it in? Like I haven't got enough to do in my busy day...
  4. Going in to configuration mode and not making changes is still logged as you making changes
    I am sure there is a good reason for this one but I can't put my finger on it. If you go in to configuration mode and then just exit out again without making any changes it is logged in the log as you having made a change. Watch this:

    R1#sh clock
    *07:11:35.710 UTC Fri Mar 1 2002
    R1#conf t
    Enter configuration commands, one per line.  End with CNTL/Z.
    R1(config)#^Z
    R1#
    *Mar  1 07:11:38.986: %SYS-5-CONFIG_I: Configured from console by bob on console
    R1#sh log
    *Mar  1 07:11:38.986: %SYS-5-CONFIG_I: Configured from console by bob on console

    This is written to the log file and also added to the top of the running config. In a multi user environment this can lead to much finger pointing when things go wrong:
    dim-witted non-technical management type: Bob, it says you changed the config yesterday a few hours before that network meltdown we had.
    bob: No, I did not change anything.
    dim-witted non-technical management type: Well it says here that you were the last person to change the config
    bob: No honestly I did not change anything. I just entered configuration mode and then exited it.
    dim-witted non-technical management type: Clear your desk bob
  5. Pipe include sometimes lies
    You can use the pipe command to filter the results of a command to make it easier to read. The pipe include command says only shows the lines including a certain string except sometimes it lies. Consider this:

    R1#sh ip route | inc .1.0
    C    192.168.11.0/24 is directly connected, FastEthernet0/0
    C    192.168.1.0/24 is directly connected, FastEthernet0/0


    The string here is ".1.0" which is in the second line but not in the first. I don't know why this happens - I assume it must be ignoring the trailing . for some reason.