19 января 2023 Linux


Наконец-то, спустя 10 лет, пришлось добавлять наши родные сертификаты, есть потенциально рабочий гайд установки корневого сертификата минцифры на RedHat/Сentos, а я представляю гайд для Debian/Ubuntu

# перейти в директорию с сертифкатами
cd /usr/local/share/ca-certificates

# скачать корневые сертификаты минцифры РФ
wget https://gu-st.ru/content/lending/russian_trusted_root_ca_pem.crt https://gu-st.ru/content/lending/russian_trusted_sub_ca_pem.crt

# обновить список доверенных сертификатов операционной системы
update-ca-certificates --verbose

Вот и всё


18 января 2023 Hardware


На плате MSI MAG Z690M MORTAR WIFI по умолчанию частота оперативной памяти равна 4 гигагерца

Но с помощью XMP профилей можно увеличить частоту обработки запросов процессора до 5,2 гигагерца, то-есть более чем на 1/4

настройка частоты оперативной памяти в BIOS

А с помощью этой команды можно узнать частоту, вольтаж и значения rank оперативной памяти установленной в материнксую плату

dmidecode --type 17

результат работы dmidecode --type 17


27 декабря 2022 19 января 2023 Hardware


Intel Core i5-12400F OEM 11630  
MSI MAG Z690M MORTAR WIFI 18524

Delete - меню БИОС

F11 - меню загрузки

Ctrl+F5 меню прошивки БИОС

MSI GTX 1050 Ti 7000 бу
MSI RTX 2060 SUPER 16000 бу
Kingston fury beast black 32gb ddr5 14799  

ADATA XPG Lancer 32Gb ddr5 (5200 PC5-41600)

11700  
MSI SPATIUM M480 [S78-440L490-P83] 1Тб nvme PCI-E 4.0 x4 устанавливается в M2_1 11999 устанавливается в M2_1 (процессорный)
Apacer AST280 [AP120GAST280-1] 120 ГБ SATA 3 1398 за 2 штуки  
Corsair rm650    
AMI BIOS7D42vBA   2022-11-11
Ubuntu 22.04    

моя сборка

 

Из особенностей:

 

  • все порты и устройства запускаются из коробки, разумеется для Nvidia необходимо ставить фирменный драйвер
  • периодически подвисает Wifi, модуль успешно сканирует сети и пытается подключиться, но подключение не происходит. не помогает ни смена Wifi точки, ни смена дистрибутива, какая то авппратная проблема и проявляется периодически, в остальное время Wifi работает
  • первый слот m2 на материнской плате предназначен только для NVME дисков, обычный SATA3.m2 там работать не будет
  • система охлаждения сильно избыточная, так как ни каким тестами и стрестестами, а так же их комбинациями не удалось разогреть CPU выше 60 градусов
  • оперативная память переключается на частоты выше 5Ггц в биосе с помощью XMP профилей
  • при передачен звука через HDMI если вслушиваться то слышен высокочастотный "призвук"
  • для данного процессора можно использовать специальные, адаптированные linux-ядра (моложе 5.18), которые будут эффективно управлять распределением нагрузки между производительными ядрам и энергоэффективными, для Ubuntu есть репозитори и скрипт для установких новых ядре, но они отказываются загружаться по причине не соответствия каким то подписями и вообще новые ядра начинают весить более 700Мб

 


26 декабря 2022 29 декабря 2022 Всякое


Для наглядности приведу три картики илюстрирующих процесс "оцифровки звука"

АЦП/ЦАП частота дискредитации звука 44,1 килогерц

на частоте 44,1 килогерца, за одну секунде будет получено данных -- 44100 * ширину слова АЦП/ЦАП, например при ширине АЦП 32 бита, за одну секунду будет получено 172 килобайта данных

на частоте 88,2 килогерца, за одну секунде будет получено данных -- 88100  * ширину слова АЦП/ЦАП, например при ширине АЦП 32 бита, за одну секунду будет получено 344 килобайта данных

на частоте 384 килогерца, за одну секунде будет получено данных -- 384000 * ширину слова АЦП/ЦАП, например при ширине АЦП 32 бита, за одну секунду будет получено 1,5 мегабайта

Соответственно, чем выше частота дискретизации и длиннее шина офицровки, тем больше накапливается данных и тем большего качества записывается или воспроизводится звук, тем больше получаются размеры аудио-файлов  и большая нагрузка необходима для обработка такого объёма данных

чем отличается цифровой звук от настоящего

Последняя картинка наглядно поясняет чем отличается цифровой звук от настоящего


26 декабря 2022 29 декабря 2022 Всякое


Тест выполняется на разных материнских платах, с разной оперативной памятью и разными процессорами

MSI X99A SLI PLUS(MS-7885), i7-6800K, DDR4 2400 MT/s


Z390 GAMING X-CF, i5-9500F, DDR4 2666 MT/s


22 декабря 2022 26 декабря 2022 Python


Два способа цеплять обработчики событий startup и shutdown

@application.on_event("startup")
def create_start_app_handler():
    print("start_app")

@application.on_event("shutdown")
def create_stop_app_handler():
    print("stop_app")


application.add_event_handler(
    "startup",
    create_start_app_handler,
)
application.add_event_handler(
    "shutdown",
    create_stop_app_handler,
)

В данном виде указанные события будут обработаны по 2 раза


22 декабря 2022 27 декабря 2022 Linux


Управление dd образами производится в 4 операции

sudo losetup --partscan --find --read-only --show  file.dd 

# создаёт виртуальный Диск и отображает имя файла виртуального диска, в моём случае /dev/loop15
# список разделов можно посмотреть вот так sudo fdisk -l /dev/loop15

sudo mount /dev/loop15p2 /mnt

# монтирует второй раздел виртуальный диска в директорую /mnt/
# тут какие то работы с данными образами

sudo umount /mnt 

# отмонтировать

sudo losetup -d /dev/loop15 

# удалить виртуальный диск

 


16 декабря 2022 Python


В инфраструктуре Flask есть фреймворк flask_restful для реализации Rest API и последние несколько лет там начались проблемы с развитием, в результате чего появилось несколько форков. В частности я решил попробовать построить один проект на flask_restx.

В этой заметке я буду описывать, что из этого вышло и насколько это решение хорошо для использования

С помощью RESTX я реализовал следующие API

  • авторизация на основе JWT токен
  • обновление JWT токена с помощью Refresh токента
  • получение и редактирование профиля пользователя
  • создание точки загрузки файла
  • загрука файла частями (chunks)
  • просмотр списка загруженных файлов
  • просмотр свойств файла по ID

Выглядит хорошо и лаконично, вот например API получения списка публикаций пользователя

Но если смотреть в целом, то приходится для каждой модели базы писать модель сериализации

А ещё больше напрягло то, что фреймворк просто не предлагает ни какого способа пагинации. То-есть, API легко отдаёт всю выборку, а для того чтобы отдать сред выборки в стиле Django, необходимо руками высчитать total и сломать весь красивый механизм marshal_with

Так же, нет интеграции со sqlalchemy, конечно можно найти способ

но как по мне конструкция ModelX.query.filter(**kwargs).all() выглядит так себе


24 ноября 2022 Всякое


Попал в руки это замечательный контроллер и ради профилактики решил заменить. Новую предлагают купить за 23 тысячи.

Но если разобраться то оказывается, там стоит обычный аккумулятор за 600 рублей от старенького Sony Ericson

https://aliexpress.ru/item/32738463692.html

https://ru.dhgate.com/product/4pcs-original-for-sony-us503759-a8h-battery/397887722.html


05 ноября 2022 25 января 2023 Linux Linux OpenVPN Docker


Если нужно быстро запустить VPN на каком то удалённом сервере то проще всего воспользоваться готовым решением https://github.com/kylemanna/docker-openvpn

Затем делаете сколько нужно VPN ключей с помощью этой команды

docker run -v $OVPN_DATA:/etc/openvpn --rm -it kylemanna/openvpn easyrsa build-client-full USERNAME nopass

выгружаете в хостовую машину

docker run -v $OVPN_DATA:/etc/openvpn --rm kylemanna/openvpn ovpn_getclient USERNAME  > USERNAME.ovpn

И всё, можно пользоваться

Чтобы в дальнейшем сохранилась возможность генерировать новые ключи необходимо восстанавливать значение export OVPN_DATA="ovpn-data-example" и использовать passkey использованный для генерации корневого сертификата

Посмотреть статус OpenVPN сервера можно вот так

docker exec -it funny_varahamihira ovpn_status

Если необходимо запустить VPN клиента на постоянной основе, например на ubuntu сервере, то необходимо сгенерировать ключ для этого сервера, скопировать его в директорию /etc/openvpn/client.conf

и разрешить автозапуск

stemctl enable openvpn@client

 


27 октября 2022 Nginx Angie


При сборке на свежей системе может возникнуть ошибка

./configure: error: the HTTP rewrite module requires the PCRE library.
You can either disable the module by using --without-http_rewrite_module
option, or install the PCRE library into the system, or build the PCRE library
statically from the source with Angie by using --with-pcre=<path> option.

Эта ошибка проявляется если сборщик не может найти библиотку перловых регулярных выражений, решается проблема легко

sudo apt install libpcre3-dev


 


10 октября 2022 СуБД awk


for t in $(echo \\dt | psql $BASE |grep public |awk '{print $3}'); do 
    echo "SELECT setval('$t"_id_seq"', (select max(id)+1 from $t), true);"; 
done | psql $BASE

Суть проблемы в том, что при репиликации таблицы значение последовательности таблицы автоматически не меняется, по этому после отключения репликации значение last_insert_id будет отставать от реального количество записей в таблице


31 августа 2022 Python


При определении LOGGING.formatters.[name].format можно использовать следующие переменные

  • args=()
  • created=1661945167.5855541
  • exc_info=None
  • exc_text=None
  • filename=views.py
  • funcName=get_context_data
  • levelname=INFO
  • levelno=20
  • lineno=585
  • module=views
  • msecs=585.5541229248047
  • msg=Rubricator
  • name=expand_settings
  • pathname=./expand_settings/views.py
  • process=2078012
  • processName=MainProcess
  • relativeCreated=8258.57162475586
  • stack_info=None
  • thread=140543658326976
  • threadName=uWSGIWorker4Core0

Из этих переменны можно составить форматированную строку

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'verbose': {
            'format': '{levelname} [{asctime}] {name}:{filename} {lineno} {message}',
        },
    }
}

Разобраться можно в файле /usr/lib/python3.10/logging/__init__.py:1100


27 июля 2022 Nginx


Недавно подключил к одному сайту ещё несколько доменов и получил вот такое значение

server_name www.server-name.ru www.server-name.it server-name.it server-name.ru servername.ru www.servername.ru www.server-name.ae www.servername.ae;

после чего сделал nginx reload и сервер начал работать как обычно, но после перезагрузки всего сервера Nginx не запустился и вы давал вот такую ошибку

nginx: [emerg] could not build server_names_hash, you should increase server_names_hash_bucket_size: 32

суть ошибки в том, у nginx по умолчанию определён небольшой размер буфера для хранения и обработки server_name

если определить размер этого буфера через переменную server_names_hash_bucket_size в разделе http файла /etc/nginx/nginx.conf то сервер будет нормально перезапускаться и работать


19 июля 2022 СуБД awk


После неудачного эксперимента с бекапами база данных может содержать по несколько копий каждой строки таблицы, это приводит любой ORM в ступор, впрочем и руками удалить такие записи не полуться, так записи имеют одинаковые ID. Ниже решение как легко удалить дубли и оставить только по одной уникальной строке
 

table_name=auth_users

data_base=breys

echo "
begin;

select count(1) from $table_name ;

create  table ttt as select distinct id x,  * from $table_name ;

alter table ttt drop  column x;

truncate table $table_name; 

insert into $table_name (select * from ttt );

drop table ttt ;

select count(1) from $table_name ;

end;
" | psql $data_base

Суть метода такова:

  1. создать копию таблицы содержающую только уникальные по ID записи, для этого используется DISTINCT,
  2. затем нужно удалить сигнальное поле x
  3. почистить целевую таблицу
  4. затем перенести данные из временной таблицы в целевую
  5. удалить временную таблицу

Если необходимо обработать все таблицы базы данных то скрипт можно использовать в цикле

for table_name in $(echo '\dt' | psql $data_base | awk '{print $3}'| grep -v ^$|sort ); do  

echo "
begin;

select count(1) from $table_name ;

create  table ttt as select distinct id x,  * from $table_name ;

alter table ttt drop  column x;

truncate table $table_name; 

insert into $table_name (select * from ttt );

drop table ttt ;

select count(1) from $table_name ;

end;
" | psql $data_base; 

done

с помощь транзакций можно предовратить удаление данных в случае проблемного импорта


29 апреля 2022 Python


Задача реализовать прозрачное связывание "союзами" пользователей сайта. Такое необходимо когда пользователь А запросил союз у пользователя Б и тот одобрил этот союз, тогда у обоих пользователей должен обновиться список союзников и табличная связь будет выглядеть следующим образом

таблица пользователей таблице связей
1 осёл
2 козёл
3 мишка
4 пятачёк
5 винипух
1 2
1 3
4 5

 

Связав таблицы где 1 колонка это номер строки хозяин, а вторая колонка это номер строки союзника получется, что у осла в союзниках козёл и мишка, а у пятачка союзник только винипух винипух

С точки зрения базы данных это связь многие ко многим и на Sqlalchemy решается через создание ассоциативной таблиц, а учитывая что помимо союзной связи необходимо сохранить состояние связи и даты создания и обновления, то ассоциативная таблица превращается в полноценную модель

class Union(db.Model):
    __tablename__ = "user_unions"
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)

    user1_id = db.Column(db.Integer, db.ForeignKey('users.id'), primary_key=True)
    user2_id = db.Column(db.Integer, db.ForeignKey('users.id'), primary_key=True)

    created = db.Column(db.DateTime(timezone=True), server_default=func.now())
    updated = db.Column(db.DateTime(timezone=True), onupdate=func.now())

    enable = db.Column(db.Boolean, default=False, nullable=False)

    def __repr__(self):
        if self.enable:
            return "союз с {} заключён".format(self.user2)
        return "союз с {} не подписан".format(self.user2)


class User(db.Model):
    __tablename__ = "users"
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(40), unique=False, nullable=False)
    email = db.Column(EmailType, unique=False)

    def add_user(self, user):
        """ добавление союза с user"""
        union = Union(user1=self, user2=user)
        db.session.add(union)
        db.session.flush()

Всё будет прекрасно до тех пор пока не потребуется у объектов User обратиться к списку союзных пользователей. Дело в том, что метол обращения к списку союзников выглядит по разному для разныз пользователей. Если для того чтобы узнать список союзников осла неоходимо выбрать все вторые колонки в строка в которых в первой колонке номер строки осла, а чтобы выбрать союзников козла, необходимо выбрать все первые колонки в строках где вторая колонка равна номеру строки козла. Таки образом получается метод выбора союзников осла и козла будет разный, а это не красиво.

Но всё таки можно реализовать универсальный метод поиск союзников для каждого пользователя и в SQL виде он будет выглядеть вот так

select * 
  from users 
  where id in (
    select user2_id id  
      from user_unions 
      where user1_id = 1 
    union 
      select user1_id id 
        from user_unions 
          where user2_id =  1
    ) 

тут производится выборка id союзников в ассоциативной таблице для обоих типов запросов, затем результаты объединяются и используются во вложенном запросе при обращении к таблице пользователей.

Sqlalchemy позволяет реализовать и такой достаточно сложный запрос с помощью объектного ORM и тогда метод выбора союзников пользователя будет выглядет следующим образом

class User(db.Model):
    __tablename__ = "users"
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(40), unique=False, nullable=False)
    email = db.Column(EmailType, unique=False)

    created = db.Column(db.DateTime(timezone=True), server_default=func.now())


    def get_all_union_users(self):
        """выборка запросов на союз"""
        q1 = db.session.query(Union.user2_id.label("id"))
        q1 = q1.filter(Union.user1==self)
        q2 = db.session.query(Union.user1_id.label("id"))
        q2 = q1.filter(Union.user2==self)
        return User.query.filter(
            User.id.in_(q1.union(q2))
        )

    def get_union_users(self):
        """выборка подтверждённых союзников"""
        q1 = db.session.query(Union.user2_id.label("id"))
        q1 = q1.filter(Union.user1==self, Union.enable==True)
        q2 = db.session.query(Union.user1_id.label("id"))
        q1 = q2.filter(Union.user2==self, Union.enable==True)
        return User.query.filter(
            User.id.in_(q1.union(q2))
        )

Проверяем результат

# проверяем список союзников у каждого пользователя стандартным способом
for u in User.query.all():u, "союзники", u.unions.all()
... 
(Пользователь: осёл , 'союзники', [союз с Пользователь: козёл  заключён, союз с Пользователь: мишка  заключён])
(Пользователь: козёл , 'союзники', [])
(Пользователь: мишка , 'союзники', [])
(Пользователь: пятачёк , 'союзники', [союз с Пользователь: винипух  заключён])
(Пользователь: винипух , 'союзники', [])


# проверяем список союзников у каждого пользователя проверяем через универсальный метод
>>> for u in User.query.all():u, "союзники", u.get_union_users().all()
... 
(Пользователь: осёл , 'союзники', [Пользователь: козёл , Пользователь: мишка ])
(Пользователь: козёл , 'союзники', [Пользователь: осёл ])
(Пользователь: мишка , 'союзники', [Пользователь: осёл ])
(Пользователь: пятачёк , 'союзники', [Пользователь: винипух ])
(Пользователь: винипух , 'союзники', [Пользователь: пятачёк ])