About Me

My photo
I know the last digit of PI

Friday, November 26, 2021

Running Kubernetes cluster on Windows

The easiest way to run kubernetes cluster on Windows is via Docker desktop Once you've installed docker dekstop click on the "Settings" / "Kubernets" and select "Enable Kubernetes".
Now you can execute kubectl command and it should succeed.
# kubectl get pods
No resources found in default namespace.

However the cluster is accessible only from the same machine since the cluster is listening on 127.0.0.1:6443 . As a workaround to allow remote connections a port proxy can be added. Use elevated command prompt and execute following command:
# netsh interface portproxy add v4tov4 listenaddress=192.168.0.42 listenport=6443 connectaddress=127.0.0.1 connectport=6443 protocol=tcp
It is important to replace the 192.168.0.42 with the machine IP address. Using 0.0.0.0 prevents the cluster from starting, so the specific IP address should be used.

Next step is to add a firewall rule that allows all connections to port 6443. Open elevated PowerShell and execute:
New-NetFirewallRule -DisplayName 'Docker Desktop Kubernetes cluster on 6443' -Profile 'Private' -Direction Inbound -Action Allow -Protocol TCP -LocalPort 6443

Copy the content of the %HOMEPATH%\.kube\config file to the remote machine and modify the host from "kubernetes.docker.internal" to "vm.docker.internal" and any occurance of the text "docker-desktop" to "remote-docker-desktop".
The final version of the config file should look like:
apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: xxxxxx
    server: https://vm.docker.internal:6443
  name: remote-docker-desktop
contexts:
- context:
    cluster: remote-docker-desktop
    user: remote-docker-desktop
  name: remote-docker-desktop
current-context: docker-desktop
kind: Config
preferences: {}
users:
- name: remote-docker-desktop
  user:
    client-certificate-data: yyyyy
    client-key-data: zzzz

On the remote machine add "vm.docker.internal" to the /etc/hosts file
192.168.0.42 vm.docker.internal
Now executing kubectl command on the remote machine should succeed.
# kubectl get pods
No resources found in default namespace.

Monday, September 14, 2020

Transfer/Export/Copy Notes from one Mac to a another

Suprisingly the Notes application that comes with MacOS lack of export/import functionality. One workaround of that limitation is using Automator. However it exports only the notes text without the attachments.

Exporting notes from Mac 1:

  1. Open Automator from the applications.
  2. Create a new Workflow
  3. Choose Run JavaScript
  4. Copy/paste following export script:
    function run(input, parameters) {
    
    	const hexEncode = function(s){
        	let result = "";
        	for (let i=0; i < s.length; i++) {
            	hex = s.charCodeAt(i).toString(16);
            	result += ("0"+hex).slice(-2);
        	}
    
        	return result;
    	};
    	
    	const pad = function(n, size) {
        	let s = n + "";
        	while (s.length < size) {
    			s = "0" + s;
    		}
        	return s;
    	};
    
    	
    	const app = Application.currentApplication();
    	app.includeStandardAdditions = true;
    	const notesApp = Application('Notes');
    	notesApp.includeStandardAdditions = true;
    
    	const notes = notesApp.notes;
    	
    	const selectedNotes = app.chooseFromList(notes.name(), { withPrompt: "Select notes to export ?", multipleSelectionsAllowed: true });
    
    
    	if (selectedNotes) {
    		var destFolder = app.chooseFolder().toString();
    		let exported = 0;
    		if (destFolder) {
    			for(let i = 0; i < notes.length; i++) {
    				if (selectedNotes.indexOf(notes[i].name()) > -1) {
    					const fileName = destFolder + "/" 
    						+ pad(i, 9) + 
    						+ "." + hexEncode(notes[i].name())
    						+ "." + notes[i].modificationDate().getTime()
    						+ "." + notes[i].modificationDate().getTime()
    						//+ "." + hexEncode(notes[i].id()) 
    						+ ".html";
    					var f = app.openForAccess(Path(fileName), { writePermission: true });
    					app.setEof(f, { to: 0 });
    					app.write(notes[i].body(), {to: f});
    					
    					app.closeAccess(f);
    					
    					//const attachments = notes[i].attachments();
    					//if (attachments.length > 0) {
    					//	attachments[0].show();
    					//	app.displayDialog(attachments[0].id().toString());						
    					//}
    					
    					exported++;
    				}
    			}
    			app.displayDialog("Done. Notes exported: " + exported);
    		}
    	}
    
    	return input;
    }
        
  5. Click Run and select notes to be exported and destination directory (preferable empty one)
  6. Copy all notes files to destination Mac 2 (keep the original file names).

Importing notes to Mac 2:

  1. Open Automator from the applications.
  2. Create a new Workflow
  3. Choose Run JavaScript
  4. Copy/paste following export script:
    function run(input, parameters) {
    	'use strict';
    	
    	
    	const hexDecode = function(s) {
    		const hexes = s.match(/.{1,2}/g) || [];
    		let res = hexes.map(h => String.fromCharCode(parseInt(h, 16))).join("");
    		return res;
    	}
    		
    	const epochToStr = function(s) {
    		const lz = function (n) { return (n <=9 ? "0" : "") + n };
     	
    		const d = new Date(parseInt(s))
    		return d.getFullYear() + "-" + lz(d.getMonth() + 1) + "-" + lz(d.getDate()) + " " + lz(d.getHours()) + ":" + lz(d.getMinutes());
    	}
    
    	const app = Application.currentApplication();
    	app.includeStandardAdditions = true;
    	
    	const response = app.displayDialog("Prefix note titles with", {
        	withIcon: "note",
        	buttons: ["Modification date", "Note index", "Don't prefix"],
        	defaultButton: "Modification date"
    	});
    	
    	//0index.1title.2create.3modify.
    	let prefixColumn = -1;
    	switch (response.buttonReturned) {
    		case "Modification date": prefixColumn = 3; break;
    		case "Creation date": prefixColumn = 2; break;
    		case "Note index": prefixColumn = 0; break;
    	}
    	
        const finder = Application('System Events');
    	const folderName = app.chooseFolder().toString();
    	const fileNames = finder.folders.byName(folderName).diskItems.name();
    	
    	const notesApp = Application('Notes');
    	notesApp.includeStandardAdditions = true;
    		
    
    	const prefix = function (fileName) {
    		const titleItems = fileName.split('.');		
    		if (prefixColumn >= 0) {
    			titleItems[2] = epochToStr(titleItems[2]);
    			titleItems[3] = epochToStr(titleItems[3]);
    			return titleItems[prefixColumn];
    		} else {
    			return titleItems[0];
    		}
    	}
    	const prefixCoeff = prefixColumn == 2 || prefixColumn == 3 ? 1 : -1;
    	fileNames.sort(function(a,b) { return prefixCoeff * prefix(a).localeCompare(prefix(b)); });
    	
    	
    
    	for (let i = 0; i < fileNames.length; i++) {
    		const path = new Path(folderName + "/" + fileNames[i]);
    
    		const titleItems = fileNames[i].split('.');
    		titleItems[2] = epochToStr(titleItems[2]);
    		titleItems[3] = epochToStr(titleItems[3]);
    		const titlePrefix = (prefixColumn >= 0 ? "[" + titleItems[prefixColumn] + "] " : "");
    		const title = titlePrefix + hexDecode(titleItems[1]);
    		     	
    		const f = app.openForAccess(path);
    		const size = parseInt(app.doShellScript("stat -f%z " + path.toString().replace(" ", "\\ ")));
    		
    		try {
    			let content = "";
    		    if (size > 0) {
    				content = app.read(f);
    			}
    		
    			notesApp.make({ new:"note", withProperties: {name: title, body:content}});			
    		} catch (e) {
    			const msg = "Error processing file: " + fileNames[i] + " (title: " + title + "). Error message:" + e.toString();
    			app.displayDialog(msg + ". Do you want to continue?");
    			notesApp.make({ new:"note", withProperties: {name: title, body:msg}});
    		} finally {
    			app.closeAccess(f);
    		}
    	}
    	
    	app.displayDialog("Done. Notes processed: " + fileNames.length);
    
    	return input;
    }
    
  5. Click Run and select the folder containing notes exported from Mac 1

Sunday, July 21, 2019

Installing remote desktop server on Fedora 30

Installing remote desktop server accessible from Windows machines requires just a few steps:
  1. Install and enable the xrdp service
    sudo dnf -y install xrdp
    
    sudo systemctl start xrdp
    
    sudo systemctl enable xrdp
    
  2. Enable firewall rules
    
    sudo firewall-cmd --add-port=3389/tcp --permanent
    
    sudo firewall-cmd --reload
    

Saturday, February 23, 2019

Cloning bigger HDD with LVM into smaller SSD

HDDtoSSD

Recently I made an upgraded to one of my servers so I swapped a regular HDD with SSD. Unfortunately the HDD is 500GB but the SSD is 480GB, so the out-of-the-box cloning software won’t work. I tried using gparted and other free software with nice GUI but ended doing the job using the command line. I used ubuntu live cd
My original HDD is /dev/sde and the new empty SSD is /dev/sdb


Overview of the steps:

  1. Analyze the existing partition table
  2. Backup MBR and partition table
  3. Shrink the LVM
  4. Resizing the partition
  5. Copying the MBR and partition table
  6. Copying the partitions

Analyze the existing partition table

root@ubunto:/# parted /dev/sde unit s print
Model: HGST HTS 725050A7E630 (scsi)
Disk /dev/sde: 976773168s
Sector size (logical/physical): 512B/512B
Partition Table: msdos
Disk Flags:
 
Number Start End Size Type File system Flags
1 2048s 7999487s  7997440s  primary ext2 boot
2 8001534s 976771071s 968769538s extended
5 8001536s 976771071s 968769536s logical lvm

So there is one primary partition for booting and one extended partition containing the LVM. Obviously it is not possible to move the extended partition directly without resizing it.

root@ubunto:/# lvm pvdisplay --map
--- Physical volume ---
PV Name /dev/sde5
VG Name vg0-11a111
PV Size  460.00 GiB / not usable 3.00 MiB
Allocatable yes
PE Size 4.00 MiB
Total PE 107519
Free PE 1280
Allocated PE 106239
PV UUID AAAAAA-bbbb-cccc-dddd-eeee-ffff-ggggg
--- Physical Segments ---
Physical extent 0 to 105564:
  Logical volume /dev/vg0-11a111/root
  Logical extents 0 to 105564
Physical extent 105565 to 107518:
  Logical volume /dev/vg0-11a111/swap
  Logical extents 0 to 1953

So the are two two logical volumes (/root and /swap). The only way to make space here is to shrink /root. It will also create a free space between /root and /swap, so /swap needs to be moved.

Backup MBR and partition table

Backup the partition table with following command:

sfdisk -d /dev/sde > part_table

It can always be restored later with:

sfdisk /dev/sde < part_table

The content of the part_table is

label: dos
label-id: 0xaaaaaaa
device: /dev/sde
unit: sectors

/dev/sde1 : start=        2048, size=     7997440, type=83, bootable
/dev/sde2 : start=     8001534, size=   968769538, type=5
/dev/sde5 : start=     8001536, size=   968769536, type=8e

The MBR (Master Boot Record) contains:

  1. Bootstrap - 446 bytes.
  2. Partition table - 64 bytes
  3. Signature - 2 bytes

So basically only the first 446 bytes needs to be backuped (the partition table is already contained in the part_table file). However it is always good to have the entire MBR. Creating a sde_mbr.bak file:

dd if=/dev/sde of=/tmp/sde_mbr.bak bs=512 count=1

It can later be restored by:

dd if=/tmp/sde_mbr.bak of=/dev/sde bs=446 count=1

or with partition table and signature

dd if=/tmp/sde_mbr.bak of=/dev/sde bs=512 count=1

Shrink the LVM

Before shrinking the volume should have enough free space.
Resizing the LVM volume with:

lvresize --resizefs --size 420G /dev/vg0-11a111/root

Now the information from pvdisplay looks like:

root@ubunto:/# lvm pvdisplay --map
--- Physical volume ---
PV Name /dev/sde5
VG Name vg0-11a111
PV Size  460.00 GiB / not usable 3.00 MiB
Allocatable yes
PE Size 4.00 MiB
Total PE 107519
Free PE 1280
Allocated PE 106239
PV UUID AAAAAA-bbbb-cccc-dddd-eeee-ffff-ggggg
--- Physical Segments ---
Physical extent 0 to 104284:
  Logical volume /dev/vg0-11a111/root
  Logical extents 0 to 104284
Physical extent 104285 to 105564:
  FREE
Physical extent 105565 to 107518:
  Logical volume /dev/vg0-11a111/swap
  Logical extents 0 to 1953

There is free space between LV /root and LV /swap , so the /swap should be moved wiht commnad line pvmove --alloc anywhere /dev/sde5:aaa-bbb /dev/sde5:ccc-ddd, where the aaa-bbb are the source physical extend and ccc-ddd are destination physical extend. It is better that ccc is equal to the end of the previous LV plus some space for safety.

pvmove --alloc anywhere /dev/sde5:105565-107518 /dev/sde5:104400-106353

After the move the pvdisplay looks like:

root@ubunto:/# lvm pvdisplay --map
--- Physical volume ---
PV Name /dev/sde5
VG Name vg0-11a111
PV Size  460.00 GiB / not usable 3.00 MiB
Allocatable yes
PE Size 4.00 MiB
Total PE 107519
Free PE 1280
Allocated PE 106239
PV UUID AAAAAA-bbbb-cccc-dddd-eeee-ffff-ggggg
--- Physical Segments ---
Physical extent 0 to 104284:
  Logical volume /dev/vg0-11a111/root
  Logical extents 0 to 104284
Physical extent 104285 to 104399:
  FREE
Physical extent 104400 to 106353:
  Logical volume /dev/vg0-11a111/swap
  Logical extents 0 to 1953
Physical extent 106354 to 107518:
  FREE

Now the last step is to resize the physical volume (note is 430G = 420GB for /root and 8G for /swap)

pvresize --setphysicalvolumesize 430G /dev/sde5

However the sde2 partition table is still having the original size, so it also needs to be resized.

root@ubunto:/# parted /dev/sde unit s print
Model: HGST HTS 725050A7E630 (scsi)
Disk /dev/sde: 976773168s
Sector size (logical/physical): 512B/512B
Partition Table: msdos
Disk Flags:
  
Number Start End Size Type File system Flags
1 2048s 7999487s  7997440s  primary ext2 boot
2 8001534s 976771071s 968769538s extended
5 8001536s 976771071s 968769536s logical lvm

This can be achieved with cfdisk application with console UI - by setting the size of the sde5 to 431GB and sde2 to 432GB.
Alternatively it can be done by altering part_table file. Each sector is 512 bytes, so 431 GB = 903872512 sectors. The end file should look like:

label: dos
label-id: 0xaaaaaaa
device: /dev/sde
unit: sectors

/dev/sde1 : start=        2048, size=     7997440, type=83, bootable
/dev/sde2 : start=     8001534, size=   903872512, type=5
/dev/sde5 : start=     8001536, size=   903872510, type=8e

The partition table could be overriten with:

sfdisk /dev/sde < part_table

Confirming the change by

parted /dev/sde unit s print
Model: HGST HTS 725050A7E630 (scsi)
Disk /dev/sde: 976773168s
Sector size (logical/physical): 512B/512B
Partition Table: msdos
Disk Flags:
  
Number Start End Size Type File system Flags
1 2048s 7999487s  7997440s  primary ext2 boot
2 8001534s 903872512s 903872512s extended
5 8001536s 903872512s 903872510s logical lvm

Copying the MBR

Next step is to copy the MBR (bootstrap + partition table)

dd if=/dev/sde of=/dev/sdb bs=512 count=1

Alternatively partition_file could be used:

sfdisk /dev/sda < part_table

or directly without using a file

sfdisk -d /dev/sde | sfdisk /dev/sda

Copying the partitions

Final step is to copy the actual data from the partitions. Since dd doesn’t have any progress indicator, the pv utility comes in handy. It is not part of the ubuntu live cd, so it must be installed

apt install pv

Finally the following commands will copy the partitions data:

dd if=/dev/sde1 | pv | dd of=/dev/sdb1
dd if=/dev/sde5 | pv | dd of=/dev/sdb5

Alternative to steps 5 and 6 are to copy the entire disk.
First find out the number of sectors of the SSD

root@ubunto:/# parted /dev/sde unit s print
Model: ATA SanDisk SDSSDA48 (scsi)
Disk /dev/sdb: 937703088s
Sector size (logical/physical): 512B/512B
...

Then copy the entire disk

dd if=/dev/sde bs=512 count=937703088 | pv | dd=of/dev/sdb

Written with StackEdit.

Thursday, June 07, 2018

Generate random password with bash script

This is a utility script that generate random password
#!/bin/bash

size=10

if [[ "$1" == "-h" || "$1" == "--help" ]]; then
  echo "randpw [size]"
  exit 0
elif [[ "$1" != "" ]]; then
  size="$1"
fi

cat /dev/urandom | env LC_CTYPE=C tr -dc '!@#.1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM' | head -c$size
echo ""

Monday, May 07, 2018

Unmount all cifs shares before sleep/hibernate/suspend

I noticed when I close the lid of my laptop and there are mounted cifs shares, it takes 2 minutes before I am able to login after resume. I track down the problem to samba trying to reconnect mounted shares. The easiest way to fix that is to unmount everything before Fedora is going to sleep. Just create a script in /usr/lib/systemd/system-sleep/mounts with following content:
#!/bin/bash

case "${1}" in
  pre)
  # your command to umount here
    for m in `mount | grep /mnt | grep // | grep cifs | awk '{print $3}'`; do 
      echo "Unmounting $m"
      umount -f $m
    done 
  ;;
  post)
  # (possibly) your command to mount here
  ;;
esac

Monday, December 25, 2017

Backup samba shares from raspbery pi box

Here is a simple script that backups samba shares from linux box.
  1. Create backup script

    Create backup script and save it as /root/backup.sh
    #!/bin/bash
    
    # Gets the latest date and time
    DATE=`date '+%Y%m%dT%H%M%S'`
    
    # The root directory that contains the backups
    BACKUP_ROOT=/home/thexman/backup
    
    # Current backup directory
    BACKUP_DIR=$BACKUP_ROOT/$DATE
    
    # Current zip file
    BACKUP_ZIP=$BACKUP_ROOT/$DATE.zip
    
    echo "Creating backup directory $BACKUP_DIR"
    
    #create backup directory if it doesn't exists
    mkdir -p "$BACKUP_DIR"
    
    echo "Mounting samba share..."
    
    # Mount windows shares to /mnt
    mount -t cifs //192.168.99.93/c$ -o "username=administrator,password=1234" /mnt
    
    echo "Creating backup copies..."
    
    # Copy files from windows machine to backup directory
    rsync -rzvh "/mnt/temp" "$BACKUP_DIR"
    #rsync "/mnt/source_directory1" $BACKUP_DIR
    #rsync "/mnt/source_directory2" $BACKUP_DIR
    #rsync "/mnt/source_directory2" $BACKUP_DIR
    
    echo "Creating zip archive $BACKUP_ZIP"
    
    # Archive the backup directory to zip file
    cd "$BACKUP_ROOT" && zip -r "$BACKUP_ZIP" "$DATE"
    
    echo "Removing backup directory $BACKUP_DIR"
    
    # Remove the backup directory
    rm -rf "$BACKUP_DIR"
    
    echo "Umounting samba shares..."
    
    # Release windows shares
    umount /mnt
    
  2. Schedule backup script

    Execute following command "sudo crontab -e" and append following at the end of the file (it will execute the script at 21:15 every day)
    # m h  dom mon dow   command
    #15 21 * * * /root/backup.sh