2022-10-10

Raspberry Pi z dyskiem SSD

Jedno z moich Raspberry Pi działa z dyskiem SSD zamiast karty microSD. Nie jest to typowa konfiguracja i kiedy kilka dni temu eksperymentując z aktualizacjami popsułem system na tyle, iż jedynym rozwiązaniem wydawała się reinstalacja uświadomiłem sobie, że nie pamiętam szczegółów. Po wejściu na stronę https://www.raspberrypi.com/software/ przekonałem się, że świat Pi dość mocno zmienił się przez ostatnie lata: pojawiło się narzędzie Imager, które znacznie ułatwia taką operację. Co prawda bez karty microSD tak całkiem się nie obejdzie, ale będzie ona potrzebna tylko na chwilę. Po uruchomieniu Imagera klikamy przycisk CHOOSE OS, wybieramy z listy "Misc utility images", następnie "Bootloader" i "USB Boot". Po wybraniu docelowej karty (przycisk CHOOSE STORAGE) klikamy WRITE - na kartę trafi oprogramowanie, którego jedynym zadaniem jest skonfigurowanie bootloadera Raspberry Pi tak, aby w pierwszej kolejności próbował uruchomić system z dysku podłączonego do portu USB.
Zapis trwa dosłownie kilkanaście sekund, wyjmujemy kartę i uruchamiamy z niej Raspberry Pi. Jeżeli mamy podpięty monitor to należy poczekać aż ekran zrobi się zielony - będzie to oznaczało, że proces zapisu bootloadera zakończył się. Gdy nie mamy monitora czekamy aż zielona dioda zacznie migać.
Następnie zapisujemy na SSD obraz pełnego systemu. Jeżeli zdecydujemy się na wersję 64-bitową należy najpierw kliknąć "Raspberry Pi OS (Other)" a następnie wybrać odpowiadającą nam odmianę systemu. Jako docelowe urządzenie tym razem wskazujemy nie czytnik kart, ale dysk podpięty przez USB - warto dwa razy upewnić się, iż wybrany jest właściwy dysk. Przed rozpoczęciem zapisu warto kliknąć ikonkę koła zębatego - moim zdaniem to najciekawsza funkcja Imagera. Pozwala wybrać wiele użytecznych opcji, które zazwyczaj trzeba było konfigurować na piechotę już po nagraniu obrazu systemu na kartę lub dysk. Można m.in. ustawić nazwę hosta, włączyć dostęp przez SSH i ustawić domyślną strefę czasową.
Po kliknięciu WRITE i nieco dłuższym oczekiwaniu niż poprzednio przepinamy dysk do Raspberry Pi, wyjmujemy kartę microSD i pełni nadziei czekamy na uruchomienie się systemu. Po około minucie host pojawi się w sieci i będzie możliwe zalogowanie się przez SSH za pomocą hasła lub klucza ustawionych za pomocą Imagera.

2022-06-11

Upgrade openHABa

Niedawno postanowiłem zrobić upgrade domowego openHABa z wersji 3.1.0 na 3.2.0. Moja instalacja działa na Raspberry Pi w kontenerze Dockerowym, a ponieważ jako katalogi z konfiguracją, pluginami i danymi mam montowane katalogi hosta, to zgodnie z oficjalną instrukcją aktualizacja powinna być banalna. Trzeba zatrzymać i usunąć kontener z aktualną wersją (wszystkie istotne dane zostają na hoście) a następnie ściągnąć i uruchomić nową. Dla porządku po usunięciu kontenera skasowałem logi z userdata/logs. Obraz wersji 3.2.0-debian ściągnął się bez problemów, zaczął uruchamiać i zawiesił się. Kontener co prawda był widoczny:
$ docker ps
CONTAINER ID   IMAGE                        ... CREATED         STATUS
e2906859d033   openhab/openhab:3.2.0-debian ... 2 minutes ago   Up 2 minutes (health: starting)
w logu userdata/update.log zapisało się:
...
Replacing userdata system files with newer versions...
Clearing cache...

Performing post-update tasks for version 3.2.0:

SUCCESS: openHAB updated from 3.1.0 to 3.2.0
ale już w logu userdata/logs/openhab.log było pusto, a po kilkunastu minutach Docker jednak uznał kontener za niezdrów:
$ docker ps
CONTAINER ID   IMAGE                        ... CREATED          STATUS
e2906859d033   openhab/openhab:3.2.0-debian ... 19 minutes ago   Up 19 minutes (unhealthy)
Komenda docker stop co prawda zadziałała, ale zapisała do syslogu mało zachęcające:
Container failed to exit within 10s of signal 15 - using the force
Przyznam się, że na tym etapie zatrzymałem się na dłuższą chwilę - nie mając nic w logach ciężko było znaleźć jakiś punkt zaczepienia. W końcu uruchomiłem kontener nie jako usługę, ale po prostu w terminalu za pomocą docker run ... dodatkowo usuwając z opcji --detach aby widzieć co dokładnie się dzieje. I stała się światłość - w ostanie linii logów, po wypisaniu której proces się zawieszał, było:
OpenJDK Client VM warning: No monotonic clock was available - timed services may be 
adversely affected if the time-of-day clock changes
na co Google od razu znalazł radę na forum openHABa i na Stack Overflow. Okazało się, że winna była zbyt stara biblioteka libseccomp2 zawarta w Debianie Buster (chodzi o hosta, nie kontener). Oczywistym rozwiązaniem byłby upgrade całego systemu do Bullseye, ale trochę obawiałem się tak radykalnej operacji, szczególnie że na oficjalnej stronie Raspberry Pi jest wprost napisane "we don’t support or recommend this". Dlatego zdecydowałem się na użycie repozytorium buster-backports, w którym jest już zaktualizowana wersja libseccomp2. Aby dodać repozytorium do systemu trzeba wykonać standardową procedurę:
$ sudo apt-key adv --keyserver keyserver.ubuntu.com \
 --recv-keys 04EE7237B7D453EC 648ACFD622F3D138
$ echo 'deb http://httpredir.debian.org/debian buster-backports main contrib non-free' \
 | sudo tee -a /etc/apt/sources.list.d/debian-backports.list
a następnie, po aktualizacji listy pakietów za pomocą:
$ sudo apt update
można zainstalować nową wersję biblioteki wskazując źródło pakietu za pomocą opcji -t:
$ sudo apt install libseccomp2 -t buster-backports
Po tej operacji ponowne uruchomienie kontenera openHAB kończy się spodziewanym:
[INFO ] [org.openhab.ui.internal.UIService   ] - Started UI on port 8080
Czekam z niecierpliwością na niespodzianki w zapowiadanej na 27. czerwca wersji 3.3.0 😅

P.S.
 
O co właściwie chodzi z owym monotonicznym zegarem? Okazuje się, że zarówno openHAB jak i Java są niewinne. W Debianie i wywodzących się z niego systemach, dla architektury arm32v7 (Raspberry Pi) kombinacja Dockera starszego niż 19.03.9 i biblioteki libseccom starszej niż 2.4.2 powoduje, iż w kontenerze nie jest dostępny poprawnie działający zegar. Wpływa to na cały system, w tym nawet komendę date (przykład ze zgłoszenia w projekcie Ubuntu):
$ docker run --rm -it --entrypoint /bin/bash ubuntu:20.04
root@8543aa82ff05:/# date
Thu Feb 26 08:20:11 UTC 1970

P.P.S.

Pierwszy wpis od 2756 dni. Z jednej strony fajnie, ale jednak trochę strasznie.

2014-11-24

Małe a cieszy

Postanowiłem sprzedać jeden z posiadanych dysków sieciowych (w celu zakupienia nowego, większego i lepszego) i stanąłem przed problemem wymazania znajdujących się danych. Wymazania, a nie skasowania czyli nie tylko usunięcia plików, ale także usunięcie pozostałości zalegających na nieużywanych obecnie obszarach dysku.

Pierwsze co zasugerował Google to komenda sfill z pakietu secure-delete. W wersji podstawowej wykonuje ona dwa przebiegi: najpierw wypełnia całe wolne miejsce losowymi bajtami, a następnie wypełnia je zerami. W wersji high-end wykonuje 38(!) przebiegów z różnymi kombinacjami danych losowych, zer i specjalnie opracowanych ciągów bajtów mających uniemożliwić odzyskanie oryginalnej zawartości. Ponieważ chciałem pozbyć się dysku jeszcze w tym roku, wybrałem wersję podstawową. Uruchomiłem sfill, zostawiłem komputer na kilka godzin i po sprawdzeniu okazało się, że wyczyszczone zostało około 1% wolnej powierzchni. Dalsze poszukiwania wskazały winowajcę: urządzenie /dev/urandom dostarczające losowych danych jest koszmarnie powolne.

Rozwiązaniem równie skutecznym - wierzę na słowo autorom dokumentacji - a znacznie szybszym okazał się OpenSSL (z małą tylko pomocą /dev/urandom). A dokładnie jakże urocze polecenie

openssl enc -aes-256-cbc -pass pass:"$(dd if=/dev/urandom bs=128 count=1 2>/dev/null | base64)" -nosalt >random.bin

zapisujące losowe dane do random.bin tak długo, aż skończy się wolne miejsce. Potem pozostało skasować utworzony plik i voilà. Dla pewności jeszcze tylko wyzerowanie wolnego miejsca:

cat /dev/zero >zero.bin

Cała operacja trwała nieco ponad dobę dla dysku o pojemności 2TB, co jest całkiem rozsądną ceną za to, iż nikt już nie odzyska moich zdjęć wujka Floyda i cioci Beth.

2014-02-09

Lista zmodyfikowanych atrybutów w Envers

Envers to część Hibernate ORM pozwalająca zapamiętywać historię zmian utrwalanych obiektów. Wystarczy dodać do klas encji kilka adnotacji, aby po każdym zapisie do głównej tabeli tworzony był wpis w tabeli historycznej pozwalający prześledzić co i kiedy zostało zmienione. Od wersji 4.1 Envers pozwala na rejestrowanie nie tylko wartości atrybutów, ale również znaczników określających czy w danej rewizji atrybut zmienił się w stosunku do rewizji poprzedniej (bez tego trzeba by było pracowicie porównywać kolejne wpisy). Niestety nie ma możliwości wygodnego odczytania tych informacji i choć dotyczące tego zgłoszenie HHH-8058 jest w JIRA od prawie roku, to nikt nie zabrał się jego realizację. Co ciekawe Envers zwraca stosowne dane, ale tylko “na potrzeby wewnętrzne” - w publicznym API nie są one już dostępne.

Dodanie stosownej funkcji okazało się bardzo proste - wymagało jedynie zmiany w klasie AuditQueryCreator tworzącej zapytania i oczywiście implementacji samego zapytania (w dużej mierze w oparciu o istniejący kod). Poprawki dla Hibernate w wersji 4.2.x i 4.3.x dostępne są w moim forku projektu hibernate-orm w branchach 4.2.HHH-8058 i 4.3.HHH-8058 w GitHubie.

Ponieważ jednak potrzebowałem działającego rozwiązania na już, a czekanie na oficjalne zaakceptowanie (lub odrzucenie :-) moich commitów może potrwać kolejny rok, popełniłem rozszerzenie Enversa, które w ogóle nie ingeruje w oryginalny kod.

<dygresja>Przy okazji odkryłem bardzo sprytną aplikację do umieszczania na stronach WWW kodów źródłowych z Githuba - efekty poniżej.</dygresja>

Nowa fabryka AuditReaderów działa dokładnie tak jak oryginalna – jedyna różnica to typ obiektu zwracanego przez metodę get:


Tak samo interfejs ExtAuditReader różni się od oryginału tylko typem zwracanym przez metodę createQuery:


Implementacja ExtAuditReadera to formalność: zmieniamy zwracany typ i zachowujemy referencję do konfiguracji (jest w oryginalnej klasie, ale prywatna):


Powoli dochodzimy do sedna. Rozszerzenie AuditQueryCreatora dodaje nową metodę forRevisionsOfEntityAndChanges(Class c, boolean selectDeletedEntities), która tworzy zapytanie zwracające nazwy zmienionych atrybutów. Jest podobna do forRevisionsOfEntity, jednak nie ma parametru selectEntitiesOnly – nie ma sensu zwracanie samych obiektów, skoro interesują nas i obiekty i lista zmian. Tak jak w ExtAuditReaderImpl musimy sami zapamiętać konfigurację i readera - są prywatne w klasie bazowej:


I w końcu właściwa implementacja zapytania. Dziedziczymy ze standardowego zapytania RevisionsOfEntityQuery, ale zmieniamy metodę list obrabiającą wyniki zapytania. Dodajemy również metodę, która wyszuka wszystkie atrybuty mające znacznik modyfikacji ustawiony na true. Na koniec dodajemy kopię metody getRevisionNumber – jest prywatna w klasie bazowej a potrzebujemy jej w list:


Składając wszystko razem możemy zapytać Enversa o nazwy zmienionych atrybutów w następujący sposób:


Kompletny projekt zawierający powyższe klasy, gotowy do zbudowanie Mavenem można ściągnąć z GitHuba, w wersjach dla Hibernate 4.2.8.Final i 4.3.1.Final.

2014-01-02

Autoreklama

A co. Mój blog to mi wolno.

Kilka dni temu Apple był uprzejmy zaakceptować moją pierwszą sensowną aplikację działającą na iOS. Nie jest to moja pierwsza aplikacja w ogóle, ale poprzednia była średnio sensowna z racji trudnej współpracy z zamawiającym i, niech jej ziemia lekką będzie, już nie jest dostępna w App Store.

Aplikacja numer dwa służy do monitorowania maszyny wirtualnej Java za pomocą doskonałej biblioteki Jolokia. Jak na razie nie potrafi zbyt wiele (tzn. wersja iOSowa, nie sama biblioteka) - wyświetla tylko ilość zajętej pamięci i obciążenie procesora:


 ale mam ambitne plany aby dodać więcej informacji.

App Store
© The Useful Pot To Keep Things In
Maira Gall