-Поиск по дневнику

Поиск сообщений в rss_rss_hh_new

 -Подписка по e-mail

 

 -Статистика

Статистика LiveInternet.ru: показано количество хитов и посетителей
Создан: 17.03.2011
Записей:
Комментариев:
Написано: 51

Habrahabr/New








Добавить любой RSS - источник (включая журнал LiveJournal) в свою ленту друзей вы можете на странице синдикации.

Исходная информация - http://habrahabr.ru/rss/new/.
Данный дневник сформирован из открытого RSS-источника по адресу http://feeds.feedburner.com/xtmb/hh-new-full, и дополняется в соответствии с дополнением данного источника. Он может не соответствовать содержимому оригинальной страницы. Трансляция создана автоматически по запросу читателей этой RSS ленты.
По всем вопросам о работе данного сервиса обращаться со страницы контактной информации.

[Обновить трансляцию]

Перевод книги Appium Essentials. Глава 4

Воскресенье, 09 Июля 2017 г. 17:58 + в цитатник
Привет. Я вернулся и подготовил для вас перевод 4й главы книги по Appium.
Если вы только включились, тут у нас предыдущие 3 главы:

Глава 4. Поиск элементов по разным локаторам

У Appium есть несколько способов локализовать элементы в мобильном приложении. В этой главе, некоторые техники поиска элементов для нативных и гибридных приложений, с использованием uiautomator и Appium inspector. Чтобы определять элементы в web-приложениях, мы рассмотрим add-on для Chrome, чтобы удаленно локализовать элементы.

В этой главе:
  1. Поиск элементов с использованием Chrome ADB plugin
  2. Поиск элементов с использованием Safari Develop
  3. Поиск элементов с использованием UIAutomator и Appium Inspector
  4. Поиск элементов по id, Name, LinkText, Xpath, cssSelector, ClassName, AccessibilityId, AndroidUIAutomator и IosUIAutomation




Поиск элементов с использованием Chrome ADB plugin


Для поиска элементов в web-приложении, необходимо установить add-on. Браузер Chrome предоставляет ADB (add-on) к исходному коду [разметке] мобильного приложения. Скачать можно отсюда.
Установив add-on, выполните следующие шаги для подключения устройства:
  1. Перейдите [в смартфоне] в Settings | About Phone и тапните по Build number 7 раз (предполагается, что у вас Android версии 4.2 или выше). Затем, вернитесь на предыдущий экран и найдите Developer options:
    (если на вашем смартфоне этот шаг ничего не дал, гуглите «How to enable developer options» и укажите модель своего телефона.)
  2. Тапните Developer options, затем на ON в настройках разработчика (всплывет диалог для подтверждения включения режима; нажмите OK); убедитесь, что чек-бокс USB debugging включен:
  3. Теперь откройте десктопную версию Chrome (предполагается, что вы уже поставили ADB плагин), кликните на иконку ADB plugin в правом верхнем углу браузера, затем, на View Inspection Targets:
  4. Подключите устройство Android к компьютеру (убедитесь, что на компьютер установлен подходящий USB-драйвер для вашего устройства). После подключения, на экране устройства должен появиться запрос на отладку по USB; нажмите OK.
  5. Откройте Chrome на девайсе и перейдите на нужный сайт (у нас это будет www.google.com).
  6. Теперь по адресу [на десктопе] chrome://inspect/#devices будут отображаться все подключенные девайсы с открытыми вкладками. Убедитесь, что Discover USB devices выбран:
  7. Кликните на inspect, чтобы открыть инструменты разработчика; Должно открыться такое окно. Кликните на иконку screencast, в правом верхнем углу, чтобы отобразить экран устройства:

Поиск элементов в iOS веб-приложениях с использованием возможности Safari Develop


Safari поставляется со встроенным решением для поиска элементов, нам остается только выполнить несколько шагов, чтобы подготовить девайс к удаленной отладке:
  1. Перейдите в Settings | Safari | Advanced:
  2. Включите Web Inspector:
  3. Откройте Safari на устройстве или симуляторе и перейдите по нужной ссылке.
  4. Теперь, откройте Safari на Mac, в меню кликните Develop, выберите нужный девайс(симулятор) (предполагается, что девайс подключен к Mac), и кликните на URL:
  5. Вы должны получить вот такой экран с разметкой:

Поиск по ID


Для взаимодействия с веб-страницей, сперва нам потребуется найти элементы на ней. Всем функциям клиента Appium client требуется какой-либо инструмент для работы с веб-страницей.
Поиск элемента по ID используется для поиска одного конкретного элемента в приложении.
findElement(By.id(String id));

Нам нужно передать ID элемента, с которым мы хотим работать. Теперь давайте узнаем ID элемента, используя плагин Chrome ADB. Ниже приведен пример работы со страницей поиска Google:
  1. В мобильном браузере Chrome, перейдите по адресу www.google.com.
  2. В плагине ADB нажмите кнопку Inspect element.
  3. Наведите курсор на строку для ввода Google-запроса, как показано на скриншоте:

Мы можем использовать выделенный id для взаимодействия с веб-элементом. Пример использования команды:
WebElement searchBox=driver.findElement(By.id("lst-ib"));

Если хотите ввести текст в поисковую строку, используйте переменную. Например, вот так:
searchBox.sendKeys("Manoj Hans");

Теперь давайте посмотрим этот же пример для работы с Safari на iOS device. Нужно выполнить следующие шаги:
  1. В Safari перейдите по адресу www.google.com.
  2. Кликните по URL под симулятором iOS во вкладке Develop браузера Safari для Mac.
  3. Кликните на иконку Inspect, а затем на поисковую строку на экране iOS симулятора, как показано здесь:


Поиск элементов по name


Еще один способ обнаружить элемент — поиск по имени; вот так будет выглядеть соответствующий метод:
findElement(By.name(String Name));

Как и в случае с id, нам нужно передать имя элемента, который мы ищем. Вернется экземпляр класса WebElement, с которым мы и будем дальше работать. Снова обратимся к примеру с Google, у поисковой строки, кроме id, есть еще и имя. Шаги такие же, как в работе с ID:

Вот как будет выглядеть команда:
WebElement searchBox=driver.findElement(By.name("q"));


Поиск элемента по linkText


Этот метод полезен, когда вы ищите на странице ссылку. Сигнатура метода:
findElement(By.linkText(String text));

Нужно передать текст, который должен быть у искомой ссылки. Так же, вернется экземпляр WebElement.
Мы попробуем найти ссылку на странице Google, у которой текст — «Images»:
WebElement imagesLink=driver.findElement(By.linkText("Images"));


Поиск по Xpath


Xpath работает и с XML и с HTML структурами для поиска элементов. Он чуть медленнее, чем методы работ с ID и именем, но это — очень хороший способ найти элемент на странице, где ID генерируется автоматически. Мы не будем подробного изучать Xpath, по необходимости, поищите туториал в Google. Сигнатура метода:
findElement(By.xpath(String XPath));

Необходимо передать Xpath для элемента, который мы ищем. Здесь мы построили Xpath, опираясь на атрибуты:
WebElement searchBox=driver.findElement(By.xpath("//input[@id='lst-ib']"));


Поиск по cssSelector


cssSelector работает строго с HTML, и он быстрее Xpath. Сигнатура метода:
findElement(By.cssSelector(String cssSelector);

В метод необходимо передать селектор элемента.
Построим cssSelector поисковой строки Google:
WebElement searchBox=driver.findElement(By.cssSelector("#lst-ib"));



Поиск элементов для нативных и гибридных приложений


Есть множество способов найти элементы для нативных и гибридных приложений, такие как UIAutomatorviewer (для Android) и Appium Inspector. Начнем с первого.

Поиск элементов с UIAutomatorviewer


UIAutomatorviewer можно найти в папке Android SDK (C:\%android-sdk%\tools); на Mac можно найти в папке tools:

При открытии, вы увидите такой экран:

Рассмотрим пример со стандартным калькулятором Android:
  1. Откройте эмулятор Android или реальное устройство.
  2. Запустите калькулятор.
  3. В окне UIAutomatorviewer, кликните на иконку скриншота девайса. Если запущено больше одного устройства, UIAutomatorviewer попросит вас выбрать девайс.

Теперь нужно найти элементы приложения, используя разные локаторы.

Поиск по ID


Сигнатура метода такая же, как поиск элемента по ID в web-приложении:
findElement(By.id(String id));

В метод передается ID элемента, с которым мы хотим работать. Ниже пример идентификации клавиши «5» в калькуляторе с использованием UI Automator Viewer:
  1. Открыть UI Automator Viewer и нажать на клавишу «5» в приложении.
  2. В разделе Node Details, вы получите resource-id вида com.android.calculator2:id/digit5:
  3. Мы можем resource-id как ID, чтобы взаимодействовать с клавишей:
    WebElement digit_5=driver.findElement(By.id("com.android.calculator2:id/digit5"));
  4. Чтобы кликнуть на клавишу, используйте команду:
    digit_5.click();


Поиск по name


Сигнатура метода нам уже знакома:
findElement(By.name(String Name));

Нужно передать имя элемента, с которым хотим работать. Ниже пример поиска кнопки DELETE:
  1. Кликнуть на кнопку DELETE в UI Automator Viewer.
  2. В разделе Node Details, вы увидите поле text со значением DELETE:
  3. Чтобы найти и кликнуть кнопку DELETE, используйте следующий код:
    delete.click();
      WebElement delete=driver.findElement(By.name("DELETE"));


Поиск по className


Сигнатура:
findElement(By.className(String ClassName));

Ниже пример поиска окна ввода по className:
  1. Кликнуть на EditBox в UI Automator Viewer.
  2. В разделе Node Detail, найти class со значением android.widget.EditText:
  3. Пример поиска элемента по className:
    WebElement editBox=driver.findElement(By.className("android.widget.EditText"));
  4. Чтобы получить значение элемента EditBox, используйте команду:
    editBox.getText();


Если этот класс используется для нескольких элементов, мы можем обратиться к элементу по индексу в возвращаемой коллекции. Например, мы хотим обратится к клавише «7» по имени класса:
List editBox=driver.findElements(By.className("android.widget.Button"));
  editBox.get(1).click();


Мы использовали метод findElements вместо findElement; так мы получим больше одного элемента по селектору. В нашем примере, у клавиши «7» значение индекса — 1. Поэтому, мы обратились к 1му элементу.

Поиск по AccessibilityId


Разработчики Appium хотели дать нам больше возможностей для локализации элементов, поэтому они создали AccessibilityId. Этот локатор определяет элементы так же, как ID и name. Сигнатура метода:
findElement(By.AccessibilityId(String AccId));

Ниже пример взаимодействия к кнопкой "+":
  1. Откройте UI Automator Viewer и кликните в калькуляторе на "+".
  2. В разделе Node Details, вы найдете content-desc со значением «plus»:
  3. Этот content-desc можно использовать как AccId:
    WebElement plusSign=driver. findElementByAccessibilityId("plus");
              plusSign.click();


Поиск по AndroidUIAutomator


AndroidUIAutomator очень мощный локатор. Он использует библиотеку Android UIAutomator для поиска элементов.
findElement(By.AndroidUIAutomator(String UIAuto));

Ниже пример взаимодействия с кнопкой "=":
  1. В UI Automator Viewer кликните на "=".
  2. В Node details мы можем выбрать любое поле. Например, resource-id со значением com.android.calculator2:id/equal. Мы можем использовать resource-id как UIAuto для работы с клавишей "=":
    WebElement equal=driver. findElementByAndroidUIAutomator("new UiSelector().resourceId(\"com.android.calculator2:id/equal\")";
  3. Другой пример — выбрать content-desc со значением equals, теперь команда будет выглядеть так:
    WebElement equal=driver. findElementBy.AndroidUIAutomator("new UiSelector().description(\"equals\")");

    Чтобы узнать больше об этом локаторе, посетите эту и эту страницу.


Поиск элементов с Appium Inspector


В предыдущей главе мы уже узнали, что Appium Inspector хорошо работает на Mac. Работая на Mac, мы можем его использовать для поиска элементов. Чтобы запустить Appium Inspector для Android, нам нужно выполнить следующие шаги:
  1. Нужно определить путь до приложения, пакета или имя активити в Appium GUI при работе с эмулятором. Если работаем с девайсом, достаточно имени пакета или активити.
    Как определить имя пакета или активити, если приложение выполняется на девайсе? Можете установить на девайс APK Info из Play Store. Если у вас есть тестируемое приложение на компьютере, то Appium server автоматически подтянет все имена после указания пути к приложени.
  2. В разделе General Settings, должна быть выбрана опция Prelaunch Application.
  3. Если вы работаете с эмулятором, тогда должна быть выбрана опция Launch AVD в разделе Android Settings (предполагается, что эмулятор вы уже создали). Если же вы работаете с девайсом, он должен быть подключен и должен быть разрешен USB debugging.
  4. Нажмите на кнопку запуска.
  5. Нажмите кнопку Inspector. Запустится Appium Inspector.

Мы уже изучили много способов нахождения элементов. Сейчас мы воспользуемся поиском по Xpath

Поиск по Xpath


Сигнатура:
findElement(By.xpath(String XPath));

Ниже пример работы с клавишей «9»:
WebElement digit_9=driver.findElement(By.xpath("//android.widget.LinearLayout[1]/ android.widget.FrameLayout[1]/ android.widget.LinearLayout[1]/ android.support.v4.view.viewPager[1]/ android.widget.LinearLayout[1]/ android.widget.LinearLayout[1]/ android.widget.Button[3]"));


Вы можете работать с переменной digit_9, чтобы взаимодействовать с элементом.
Теперь о том, как работать с Appium Inspector для iOS:
  1. В Appium GUI нужно указать путь до приложения.
  2. Опция Prelaunch Application должна быть включена ( в разделе General Settings).
  3. Если вы работаете с симулятором, должна быть еще и включена опция Force Device в разделе iOS Settings.
  4. Нажмите на кнопку запуска.
  5. Нажмите на кнопку Inspector.

В примере мы будем работать с TestApp, его можно скачать с Appium GitHub.

Поиск по name


Сигнатура метода:
findElement(By.name(String Name));

Ниже пример работы со вторым EditBox в приложении TestApp:
  1. Кликните на второй EditBox в Appium Inspector.
  2. В вкладке Details найдите поле name со значением IntegerB. Мы можем использовать значение для обнаружения второго EditBox:
    WebElement editBox=driver.findElement(By.name("IntegerB"));
  3. Чтобы ввести текст в EditBox, используйте команду:
    editBox.sendKeys("12");



Поиск по IosUIAutomation


UIAutomation — JavaScript библиотека, использующаяся для поиска элементов в Apple's Automation Instruments. Разработчики Appium дали нам похожий способ поиска элементов в Appium, используя IosUIAutomation:
findElements(By.IosUIAutomation(String IosUIAuto));

Для того, чтобы начать использовать IosUIAutomation, нужно:
  1. Нужно передать значение IosUIAuto для элемента, с которым хотим работать. Ниже пример поиска первого EditBox в приложении TestApp:
    WebElement editBox=driver. findElements(By.IosUIAutomation(".elements()[0]")); //Здесь '0' - это индекс элемента
  2. Чтобы ввести текст в первый EditBox, используем команду:
    editBox.sendKeys("10");
  3. Другой пример — поиск элемента по textFields:
    WebElement editBox=driver. findElements(By.IosUIAutomation(".textFields()[0]"));

Чтобы узнать больше о библиотеке UIAutomation, посетите эту страницу.


Вот и все. пришло время автоматизировать наше приложение. В следующей главе, мы будем работать с Appium driver.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/332706/


Метки:  

[Из песочницы] Программный сбор данных о котировках

Воскресенье, 09 Июля 2017 г. 16:36 + в цитатник
Заголовок обязывает перейти непосредственно к программному коду… Но, думаю, все же необходима вводная часть. А зачем, собственно, это нужно?

Эффективные действия на бирже связаны с тщательным анализом происходящего на рынке. Что кроется за динамикой цифр, котировок?

Отсутствие такого анализа, либо сумбурное принятие решений по сделке может привести к потерям. Мне не раз приходилось наблюдать за тем, как люди принимали решения — правильные… или не правильные — в дилинговом зале брокерской конторы.

Дилинговые залы брокерских контор… там существует своя, особая атмосфера. Атмосфера общения, обмена опытом, эмоциями. Мне нравятся дилинговые залы. По тому как человек входит в сделку, трейдеров можно разделить на две группы. Я буду говорить о тех, чей результат, как правило, печален. И таких трейдеров — большинство. Итак — описываю процесс входа в рынок трейдера соответствующей группы. В дилинговый зал вбегает мужчина лет 20-60 выкрикивает: «Куда идем?! Вверх?! Вниз?!» Со стороны встречающих слышаться неоднозначные выкрики «Вверх! Вниз!» Новоприбывший присоединяется к наиболее громко крикнувшей группе и… делает ТЫЦ. ТЫЦ по кнопке покупки или продажи. Все. Теперь человек в рынке. С этого момента он рискует своими деньгами. С этого момента трейдер не похож на трейдера. Он похож на болельщика. Вувузела в руках такого трейдера, думаю, была бы уместным инструментом торговли.


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

Результат таких сделок вполне предсказуем. Но… есть ли более счастливый исход? Конечно. И связан он с анализом данных котировок. Как получить эти данные? Как получить эти данные в больших объемах? Как здорово, что есть такая замечательная компания «ФИНАМ» и их интернет-ресурс finam.ru! Сервера «ФИНАМ» предоставляют замечательную возможность — скачивать котировки, например вот по такой форме (например):



Однако, таким образом предоставляется возможность скачать лишь один файл за одну загрузку. А что если мы хотим получить больше данных для анализа? Гораздо больше? Практически по всем инструментам! По всем периодам! Это даст богатейшие возможности для анализа данных. Оу… возможно ли такое? Ответ: да возможно.

Пока же определимся с перечнем бумаг (инструментов), а также с основными принципиальными моментами, которые позволят нам получить данные о котировках. Перечень бумаг (инструментов) которые предоставляться компанией «ФИНАМ» будем брать отсюда:



Эта страница интересна для нас тем, что на ней есть, во-первых, большая часть инструментов которые дает «ФИНАМ»; во-вторых, веб-ссылки, по которым можно перейти непосредственно на страницу каждой ценной бумаги (инструмента).

Ссылки имеют следующий вид:

www.finam.ru/profile/moex-akcii/polymetal-international-plc/export
www.finam.ru/profile/moex-akcii/pllc-yandex-n-v/export
www.finam.ru/profile/moex-akcii/alrosa-ao/export

Пропарсив соответствующую станицу получим файл ссылок. Теперь мы знаем где «живут» инструменты. Файл можете скачать по этой ссылке. Зачем нам место жительства каждого инструмента? Этот параметр нам еще пригодится. Запаситесь терпением. Пока имеем ссылки по 6131 бумаге (инструменту).

Что требует сервер «ФИНАМ»? Какие параметры для получения данных? Давайте попробуем получить один файл, и посмотрим параметры запроса. Скачивая котировки компании Polymetal, имею вот такой GET запрос:
__http://export.finam.ru/POLY_170620_170623.txt?market=1&em=175924&code=POLY&apply=0&df=20&mf=5&yf=2017&from=20.06.2017&dt=23&
mt=5&yt=2017&to=23.06.2017&p=8&f=POLY_170620_170623&e=.txt&cn=POLY&dtf=1&tmf=1&
MSOR=1&mstime=on&mstimever=1&sep=1&sep2=1&datf=1&at=1

Среди всего перечня хотелось бы акцентировать внимание на параметрах em, market, code. Параметр em следует понимать как индекс, своеобразную метку бумаги (инструмента). Если мы хотим скачивать не один инструмент, а массив данных по нескольким бумагам (инструментам) мы должны знать em каждого из них. Переменная market говорит о том, где вращается данная бумага (инструмент) – на каком рынке? Маркетов много: МосБиржа топ***, МосБиржа пифы***, МосБиржа облигации***, Расписки и т.д. Параметр code – это символьная переменная по инструменту.

Итак, для получения файла котировок нам нужно добыть эти три параметра: em и market и code. По всем бумагам (инструментам). Вопрос — где их взять? Ответ: вспоминаем о файле со ссылками. В файле есть, например, такая ссылка:

www.finam.ru/profile/moex-akcii/polymetal-international-plc/export

Зайдем на нее и в исходном коде страницы увидим то, что нам нужно — в элементах javascript сидят наши искомые параметры, которые относятся к данной бумаге (инструменту):

Finam.IssuerProfile.Main.issue = {"quote": {"id": 175924, "code": "POLY", "fullUrl": "moex-akcii/polymetal-international-plc", "title": "Polymetal", "decp": 1, "testDriveEnabled": false, "market": {"id": 1, "title": "МосБиржа акции", "volumeEnabled": true},"info": {"decp": 1, "last": 680, "pchange": 1.87266, "change": 12.50001, "bid": null, "ask": null, "open": 668, "high": 686, "low": 666, "close": 667.5, "volume": 53037, "date": "05.07.2017 18:47:18", "weekMin": 653.5, "weekMax": 688, "monthMin": 653.5, "monthMax": 753, "yearMin": 572, "yearMax": 1009.5,"currency": "руб.","volumeCode": "шт."},"
/*…тут еще куча важных параметров, но они нам не нужны …*/
 175924, "url": "/profile/moex-akcii/polymetal-international-plc/secondary/", }, "corporativeEvents": {"quote": 175924, "url": "/profile/moex-akcii/polymetal-international-plc/corporate/", }, "blogsAndGraphs": {"quote": 175924, "url": "__http://whotrades.com/markets/instrument/polymetal-international-plc", "count": "1", "pageSize": 1, "pageNumber": 1, "pagesCount": 1}}};

Заметим, что в данном кусочке кода id — это и есть em; имеется параметр code, а также параметры маркета – id и его русскоязычное название. Данный кусок кода с вариациями присутствует у каждого бумаги (инструмента). Сходим, например, на:

www.finam.ru/profile/moex-akcii/pllc-yandex-n-v/export
www.finam.ru/profile/moex-akcii/alrosa-ao/export

и увидим все то же самое. Теперь, думаю, общая цепочка получения данных понятна: в цикле перебираем ссылки, где живут отдельные бумаги (инструменты). Парсим кусочки javascript, собирая параметры em, market и code для каждой позиции. Имея на руках эти данные, можем программно заходить на сервер «ФИНАМ» и получать файлы котировок. Осталось дело за техникой исполнения.

Чем будем парсить? Парсить будем, используя Java. И… из всех велосипедов я выбираю тот, который стоит у меня в гараже. А именно Jsoup. Хотя можно было бы использовать и htmlunit.



Небольшое уточнение. При парсинге страницы мною были получены также данные – русскоязычное название бумаги (1) и раздел, в который «ФИНАМ» определили данную бумагу (инструмент) (2). Таким образом, на входе парсера имеется три файла. Напомню, имеем 6131 позиций — бумаг (инструментов). Всю эту информацию, а также результаты парсинга объединим в один файл. Код парсера можно скачать по этой ссылке.

В результате выполнения имеем файл function_parameters.csv. Каждая строка файла при построчном считывании может использоваться как перечень параметров для функции обращения к серверу «ФИНАМ» за котировками. Файл function_parameters.csv можно скачать по этой ссылке.

Для того чтобы написать функцию обращения к серверу «ФИНАМ» (а писать мы будем ее на Python), еще раз рассмотрим параметры GET запроса:
__http://export.finam.ru/POLY_170620_170623.txt?market=1&em=175924&code=POLY&apply=0&df=20&mf=5&yf=2017&from=20.06.2017&dt=23&
mt=5&yt=2017&to=23.06.2017&p=8&f=POLY_170620_170623&e=.txt&cn=POLY&dtf=1&tmf=1&
MSOR=1&mstime=on&mstimever=1&sep=1&sep2=1&datf=1&at=1

POLY_170620_170623 – очевидно, что данная строка представляет параметр code, а также временные характеристики.

.txt – расширение файла; расширение упоминается в параметре e; при написании функции следует помнить об этом нюансе.

Примем также во внимание содержимое исходного кода страницы типа www.finam.ru/profile/moex-akcii/gazprom/export внутри тэга form (где name=«exportdata»). Характеризуем показатели.

market, em, code – об этих параметрах, упоминал ранее, при обращении к функции их значения будут приниматься из файла.
df, mf, yf, from, dt, mt, yt, to – это параметры времени.
p — период котировок (тики, 1 мин., 5 мин., 10 мин., 15 мин., 30 мин., 1 час, 1 день, 1 неделя, 1 месяц)
e – расширение получаемого файла; возможны варианты — .txt либо .csv
dtf — формат даты (1 — ггггммдд, 2 — ггммдд, 3 — ддммгг, 4 — дд/мм/гг, 5 — мм/дд/гг)
tmf — формат времени (1 — ччммсс, 2 — ччмм, 3 — чч: мм: сс, 4 — чч: мм)
MSOR — выдавать время (0 — начала свечи, 1 — окончания свечи)
mstimever — выдавать время (НЕ московское — mstimever=0; московское — mstime='on', mstimever='1')
sep — параметр разделитель полей (1 — запятая (,), 2 — точка (.), 3 — точка с запятой (;), 4 — табуляция (»), 5 — пробел ( ))
sep2 — параметр разделитель разрядов (1 — нет, 2 — точка (.), 3 — запятая (,), 4 — пробел ( ), 5 — кавычка ('))
datf — Перечень получаемых данных (#1 — TICKER, PER, DATE, TIME, OPEN, HIGH, LOW, CLOSE, VOL; #2 — TICKER, PER, DATE, TIME, OPEN, HIGH, LOW, CLOSE; #3 — TICKER, PER, DATE, TIME, CLOSE, VOL; #4 — TICKER, PER, DATE, TIME, CLOSE; #5 — DATE, TIME, OPEN, HIGH, LOW, CLOSE, VOL; #6 — DATE, TIME, LAST, VOL, ID, OPER).
at — добавлять заголовок в файл (0 — нет, 1 — да)

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

# -*- coding: utf-8 -*-
"""
Created on Sat Jun 24 01:46:38 2017

@author: optimusqp
"""
import urllib


code='POLY';
e='.txt';
market='1'
em='175924';
e='.txt';
p='3';
yf='2017';
yt='2017';
month_start='05';
day_start='20';
month_end='06';
day_end='20';
dtf='1';
tmf='1';
MSOR='1';
mstimever='0'
sep='1';
sep2='3';
datf='1';
at='1';


year_start=yf[2:];
year_end=yt[2:];
mf=(int(month_start.replace('0','')))-1;
mt=(int(month_end.replace('0','')))-1;
df=(int(day_start.replace('0','')))-1;
dt=(int(day_end.replace('0','')))-1;


def quotes(code,year_start,month_start,day_start,year_end,month_end,day_end,e,market,em,df,mf,yf,dt,mt,yt,p,dtf,tmf,MSOR,mstimever,sep,sep2,datf,at):
    
    page = urllib.urlopen('http://export.finam.ru/'+str(code)+'_'+str(year_start)+str(month_start)+str(day_start)+'_'+str(year_end)+str(month_end)+str(day_end)+str(e)+'?market='+str(market)+'&em='+str(em)+'&code='+str(code)+'&apply=0&df='+str(df)+'&mf='+str(mf)+'&yf='+str(yf)+'&from='+str(day_start)+'.'+str(month_start)+'.'+str(yf)+'&dt='+str(dt)+'&mt='+str(mt)+'&yt='+str(yt)+'&to='+str(day_end)+'.'+str(month_end)+'.'+str(yt)+'&p='+str(p)+'&f='+str(code)+'_'+str(year_start)+str(month_start)+str(day_start)+'_'+str(year_end)+str(month_end)+str(day_end)+'&e='+str(e)+'&cn='+str(code)+'&dtf='+str(dtf)+'&tmf='+str(tmf)+'&MSOR='+str(MSOR)+'&mstimever='+str(mstimever)+'&sep='+str(sep)+'&sep2='+str(sep2)+'&datf='+str(datf)+'&at='+str(at))
    f = open("company_quotes.txt", "w")
    content = page.read()
    f.write(content)
    f.close()

qq = quotes(code,year_start,month_start,day_start,year_end,month_end,day_end,e,market,em,df,mf,yf,dt,mt,yt,p,dtf,tmf,MSOR,mstimever,sep,sep2,datf,at)

Код функции можно скачать также по этой ссылке.

Что дальше? Теперь возможно использовать данную функцию в цикле по имеющимся у нас позициям. Всего имеем, напомню, 6131 позицию. Из файла function_parameters.csv подгружаем параметры, указываем дату, выбираем нужный формат. И, используя данный код, не забудьте о правилах хорошего тона – поставьте задержку в пару секунд в итерацию цикла, дабы не перегружать сервер-источник.

Данных для анализа рынка, думаю, у вас будет предостаточно. Искренне надеюсь, что клиентов у компании «ФИНАМ» после написания данной статьи только прибавится!
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/332700/


Метки:  

Дайджест интересных материалов для мобильного разработчика #210 (03 июля — 09 июля)

Воскресенье, 09 Июля 2017 г. 16:09 + в цитатник
На прошлой неделе мы обсуждали Core ML, борьбу со спамерами, Kotlin, UX, онбординг и многое другое. Ну и, с совсем небольшим допущением, в подборке для Android впервые за всю историю случился своеобразный «парад планет» :).



Тестирование и обзор Core ML

На WWDC’17 Apple представила новый фреймворк для работы с технологиями машинного обучения Core ML. На основе него в iOS реализованы собственные продукты Apple: Siri, Camera и QuickType. Core ML позволяет упростить интеграцию машинного обучения в приложения и создавать различные «умные» функции с помощью пары строчек кода.

Сказ о том, как Android-разработчика спамеры задолбали, и что и из этого вышло

Далее через сайт, либо по телефону вы передаете показания. Но как обычно, ко всякому благому делу присасываются кровопийцы и мошенники разных мастей. Чтобы облегчить себе жизнь, и усложнить её спамерам и мошенникам я написал приложение «Журнал счетчиков».

Дайджест доступен и в виде рассылки. Подписаться вы можете тут.

iOS


Android


Windows


Разработка


Аналитика, маркетинг и монетизация


Устройства, IoT, AI



< Предыдущий дайджест. Если у вас есть другие интересные материалы или вы нашли ошибку — пришлите, пожалуйста, в почту.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/332698/


[recovery mode] Настройка BGP Looking glass на базе OpenBSD 6.1

Воскресенье, 09 Июля 2017 г. 13:49 + в цитатник
Мы уже давно использовали связку OpenBSD+OpenBGPD+bgplg для предоставления публичного сервера bgp looking glass. Было принято обновить OpenBSD до свежей версии.

В процессе настройки выяснилось несколько ньюансов, не раскрытых в полной мере в официальной документации. В результате получилась вот такая инструкция по настройке сервиса BGP looking glass на базе свежеустановленной OpenBSD 6.1

0. Устанавливаем OpenBSD

1. Кладем SSL-ключ в /etc/ssl/private/server.key и цепочку сертификатов в /etc/ssl/server.crt

2. Настраиваем /etc/httpd.conf

ext_addr="0.0.0.0"
ext_addr6="::"
prefork 2
domain="lg.example.net"

server $domain {
        listen on $ext_addr port 80
        listen on $ext_addr6 port 80
        block return 301 "https://$domain$REQUEST_URI"
}
server $domain {
        listen on $ext_addr tls port 443
        listen on $ext_addr6 tls port 443
        tls {
                certificate "/etc/ssl/server.crt"
                key "/etc/ssl/private/server.key"
        }
        location "/cgi-bin/*" {
                fastcgi
                root ""
        }
        location "/" {
                block return 302 "/cgi-bin/bgplg"
        }
}


3. Настраиваем /etc/bgpd.conf

AS XXX
fib-update no
listen on 0.0.0.0
route-collector yes
router-id A.B.C.D

socket "/var/www/run/bgpd.rsock" restricted

neighbor D.E.F.G {
 remote-as XXX
 descr "r1"
 announce none
}
neighbor D:E:F::G {
 remote-as XXX
 descr "r1v6"
 announce none
}


4. Выставляем права, настраиваем chroot. Последней командой вы разрешаете выполнение ping и traceroute с вашего сервиса, однако при этом устанавливаете флаг SUID на исполняемые файлы.

chmod 0555 /var/www/cgi-bin/bgplg
chmod 0555 /var/www/bin/bgpctl
mkdir /var/www/etc
cp /etc/resolv.conf /var/www/etc
chmod 4555 /var/www/bin/ping* /var/www/bin/traceroute*


5. Если на предыдущем шаге вы включали ping и traceroute, то проверьте /etc/fstab на отсутствие флага nosuid для /var. Не забудьте перемонтировать /var либо перезагрузиться.

6. Настраиваем pf.conf

ext_if = "vio0"

table  { 192.168.0.0/24 2001:67c:aaaa::/64 }
table  { 192.168.2.0/24 2001:67c:bbbb::/64 }

set block-policy drop
set skip on lo

#block return   # block stateless traffic
#pass           # establish keep-state

match in all scrub (no-df random-id max-mss 1440)

block all

pass out quick
pass in on egress proto tcp from  to (egress) port { 22 }
pass in on egress proto tcp from  to (egress) port { 179 }
pass in on egress proto tcp from any to (egress) port { 80 443 }
pass in on egress proto icmp from any to (egress)
pass in on egress proto icmp6 from any to (egress)


7. Запускаем демоны
rcctl enable httpd
rcctl enable slowcgi
rcctl enable bgpd
rcctl start httpd
rcctl start slowcgi
rcctl start bgpd
pfctl -f /etc/pf.conf


8. Вуаля!
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/332694/


Метки:  

Автоматизация IP-сети. Часть2 – Мониторинг скорости открытия Веб страниц

Воскресенье, 09 Июля 2017 г. 13:49 + в цитатник
image alt Продолжаем серию статей по доступной автоматизации в IP-сети. У каждого из инженеров, работающих с сетью Интернет, так или иначе периодически возникает потребность измерения скорости загрузки Веб странницы. Для этого существует множество инструментов, один из них это утилита wget. Например, для измерения скорости загрузки можно из консоли (Unix/Linux) воспользоваться такой командой:
[root@localhost ~]# wget -E -H -p -Q300K --user-agent=Mozilla --no-cache --no-cookies --delete-after --timeout=15 --tries=2 habrahabr.ru 2>&1 | grep Downloaded
Downloaded: 7 files, 411K in 0.3s (1.22 MB/s)

Следует отметить, что ограничение Q300K достаточное чтобы понять скорость загрузки, особенно если сетевая задержка до сайта более 10мс. При этом результаты скорости будут не очевидны если количество скаченной информации меньше 100K.
      Но что, если есть потребность отслеживать скорость загрузки ресурса на постоянной основе? Вопрос можно попробовать решить при помощи небольшого скрипта, системы мониторинга (умеющей запускать скрипты) и гидроизоляционного скотча.
Для написания скрипта возьмем Python3, в качестве системы для примера возьмем Cacti 0.8.8h.
Скрипт будет выглядеть следующим образом:
Код Python
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import datetime
import re
import os
import subprocess
import argparse

parser = argparse.ArgumentParser()
parser.add_argument("-h_page", "--hostname_page", dest = "hostname_page")
args = parser.parse_args()

curent_time=str(datetime.datetime.now().strftime("%Y-%m-%d_%H:%M:%S_"))
pid=os.getpid()

fweb=open('/usr/TEST_Script/TMP_FILES/web_temp'+curent_time+str(pid)+'.txt', 'w')
web=subprocess.call(["timeout 120 wget -E -H -p -Q300K --user-agent=Mozilla --no-cache --no-cookies --delete-after --timeout=15 --tries=2 "+args.hostname_page+" 2>&1 | grep '\([0-9.]\+ [KM]B/s\)'"], bufsize=0, shell=True, stdout=(fweb))
fweb.close()
fweb=open('/usr/TEST_Script/TMP_FILES/web_temp'+curent_time+str(pid)+'.txt', 'r')
data=fweb.read()
os.remove('/usr/TEST_Script/TMP_FILES/web_temp'+curent_time+str(pid)+'.txt')
speed_temp=re.findall(r's \((.*?)B/s', str(data))
speed_temp_si=re.findall(r's \((.*?) [KM]B/s', str(data))
try:
        if re.findall(r'M', str(speed_temp))==[] and re.findall(r'K', str(speed_temp))==[]:
                speed_="{0:.3f}".format(float(speed_temp_si[0])*0.001*8)
        elif re.findall(r'M', str(speed_temp))!=[]:
                speed_="{0:.3f}".format(float(speed_temp_si[0])*1000*8)
        elif re.findall(r'K', str(speed_temp))!=[]:
                speed_="{0:.3f}".format(float(speed_temp_si[0])*1*8)
except:
        speed_='no_data'
print ('web_speed_test:'+speed_)


Для проверки работоспособности, его можно запустить с консоли:
[root@localhost ~]# python3.3 /usr/TEST_Script/web_open.py -h_page habrahabr.ru
web_speed_test:10160.000

Теперь необходимо выполнить стандартные манипуляции в Cacti для запуска скриптов, они хорошо задокументированы на сайте Cacti, также можно воспользоваться подсказкой ниже.
комиксы Cacti
1. Console -> Data Input Methods

2. Console -> Data Templates

3. Console -> Graph Templates

4. Console -> Devices

5. Console -> Create New Graphs



Если все сделано правильно, в итоге должны получиться подобные графики:

Тонкая линия — это измеренные данные, имеют пилообразный вид т.к. измерение в данном случае происходит одномоментно и отображается на графике, в то время как при загрузке например сетевых интерфейсов мы видим усредненное значение скорости за измеряемый период (обычно по умолчанию 5 минут). Толстая линия — это линия тренда, которая помогает аппроксимировать визуализацию. На представленном графике, реально измеренные значения для одного из Веб сайтов. Просадка по центру, подтвержденные администратором сайта технические неполадки и далее частичное восстановление.
     Если Вы нашли для себя эту статью полезной, то возможно в этом разделе Вам также будут интересны следующие возможности нестандартного мониторинга:
— Мониторинг TCP retransmission при измерении скорости загрузки сайта.
— Мониторинг ICMP RTD с любого маршрутизатора Вашей Интернет сети (Juniper, Huawei, Cisco) до Веб ресурсов.
— Мониторинг RTD задержки до сайтов, с закрытым протоколом ICMP.
     Пишите в комментариях если нужно продолжение.
     Спасибо за потраченное время.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/332690/


Метки:  

Алгоритм поиска наилучшего маршрута в linux

Воскресенье, 09 Июля 2017 г. 13:45 + в цитатник
В настоящее время в компьютерных сетях практически повсеместно используется протокол IP. Для того, чтобы отправить IP-пакет каждый маршрутизатор ищет в свой таблице маршрутизации наилучший маршрут для адреса назначения пакета. В данной статье я хочу описать алгоритм поиска наилучшего маршрута, реализованного в ядре linux.

Обычно говорят, что все маршруты находятся в таблице маршрутизации, в которой и выполняется поиск наилучшего маршрута. Здесь может возникнуть терминологическая путаница. Дело в том, что под таблицей маршрутизации можно понимать две разные вещи, которые иногда обозначаются терминами Routing Information Base (RIB) и Forwarding Information Base (FIB). И RIB и FIB действительно хранят ip-маршруты, но предназначение этих структур и алгоритмы выбора лучших маршрутов в этих структурах отличаются.

Routing Information Base (RIB)
Маршруты на маршрутизатор могут попадать из различных источников: быть настроены статически, попадать из протоколов динамической маршрутизации OSPF, BGP и т.д. При этом из различных источников могут приходить маршруты с одним и тем-же префиксом. Из этих маршрутов нужно выбрать какой-то один и добавить его в FIB. Эта задача как раз и решается в RIB. Обычно маршрутам из разных источников присваивается разная Административная Дистанция и наилучшим считается маршрут с наименьшей Административной Дистанцией. Важно, что конкурируют между собой маршруты с одним и тем-же префиксом. Маршруты с разными префиксами между собой не сравниваются. Например, префиксы 10.0.0.0/8 и 10.0.0.0/16 это разные префиксы и в FIB попадут оба маршрута. Таким образом, в RIB выполняется поиск КОНКРЕТНОГО префикса. Поиск префиксов в RIB выполняется при добавлении или удалении маршрутов, что происходит относительно редко.

Forwarding Information Base (FIB)
FIB служит собственно для маршрутизации ip-пакетов. Здесь лучшим маршрутом считается маршрут с наиболее длинным префиксом, совпадающим с адресом назначения в ip-пакете. Совместная работа RIB и FIB показана на рисунке:


Рисунок 1. Совместная работа RIB и FIB. В RIB попадают и отбираются маршруты от разных протоколов маршрутизации. В FIB выполняется маршрутизация пакетов согласно наиболее длинным префиксам.

Поиск лучшего маршрута в FIB происходит для каждого нового ip-пакета. От того, насколько эффективно происходит поиск маршрута в FIB напрямую зависит скорость маршрутизации устройства. Поэтому поиск в FIB должен производиться максимально быстро и эффективно. В FIB может содержаться несколько сот тысяч маршрутов с разными префиксами, поэтому задача выглядит довольно нетривиальной. Для ее оптимизации было предложено много различных алгоритмов. В настоящей статье я хочу рассказать, как поиск в FIB реализован в ядре linux.

Формулировка задачи
IP-префикс обычно обозначают в виде ip-адреса и длины префикса, например: 10.0.0.0/8. Для нас будет удобнее представлять префикс в двоичном виде, оставив только левую часть, равную длине префикса и опустив его правую несущественную часть: 00001010. Маршрут по-умолчанию (т.е. префикс нулевой длины будем обозначать звездочкой *. Задача поиска выглядит следующим образом. У нас есть набор различных префиксов: *, 00, 10, 0000, 1000, 1001, 1010, 10111. Нам дается адрес назначения, который в двоичном виде выглядит, например, так: 1010110001110100010001000010111. Среди имеющихся префиксов нужно найти лучший, т.е. наиболее длинный, который совпадает с левой частью адреса назначения. В нашем примере это будет префикс 1010. К каждому префиксу привязан ip-адрес следующего маршрутизатора (next-hop), исходящий интерфейс и другая информация, достаточная отправки пакета дальше. В дальнейшем, для простоты, я буду использовать вышеуказанный набор префиксов и адреса назначения длины 6. Все нижеизложенное также применимо и для реальных ip-адресов длины 32. Как может выглядеть реальный список префиксов в FIB (в двоичном виде) можно посмотреть здесь.

Мы будем последовательно строить алгоритмы поиска лучшего префикса начиная с самого простого, постепенно улучшая их, пока не придем к алгоритму, используемому в ядре linux.

Тривиальный алгоритм
Очевидным и самым простым алгоритмом является простой перебор всех имеющихся префиксов и выбора из них лучшего. Такой алгоритм легко реализовать, но на большом количестве префиксов скорость его работы совершенно неудовлетворительна. Если в FIB у нас содержится 500000 префиксов, то нам нужно перебрать и проверить их всех.

Скорость поиска можно несколько повысить, храня префиксы в порядке убывания их длины. Тогда первый совпадающий префикс будет самым длинным и, следовательно, лучшим.

Префиксное дерево
Естественным способом организации префиксов является префиксное дерево (trie). Пример префиксного дерева для префиксов *, 00, 10, 0000, 1000, 1001, 1010, 10111 приведен на рисунке:


Рисунок 1. Использование префиксного дерева для поиска наиболее длинного префикса. Красными стрелками показана последовательность поиска для адреса 101100.

Белые узлы соответствуют интересующим нас префиксами. В них также хранится ссылка на информацию о том куда отправлять пакет дальше (next-hop и т.д.). Желтые узлы являются промежуточными и служат для правильной организации структуры дерева. Верхний узел является корнем дерева и соответствует префиксу 0.0.0.0/0. Для поиска лучшего префикса проверяется самый левый бит адреса назначения пакета. Если он равен 0, то спускаемся налево, если равен 1, то направо. Далее проверяем второй бит и т.д., постепенно спускаясь по дереву пока есть возможность. Спускаясь по дереву, запоминаем последний пройденный белый узел, он и будет наилучшим. Например, при поиске наилучшего префикса для адреса 101100 последовательно проходим узлы *, 1, 10, 101, 1011. Последний пройденный белый префикс равен 10, он и будет наилучшим.

Максимальное количество сравнений, очевидно, не превышает высоты дерева и для ipv4 равняется 32.

Сжатие пути
Если префиксов не очень много, то в префиксном дереве могут образовываться цепочки без ветвлений. В такой цепочке можно пропустить желтые узлы с одним дочерним префиксом, как показано на рисунке. Этот механизм называется сжатие пути (Path compression) а дерево построенное таким образом — Path compressed trie (PC-trie).


Рисунок 2. Использование Path compressed trie для поиска наиболее длинного префикса для адреса 101100.

Алгоритм поиска остается практически без изменений. Для поиска наилучшего префикса для адреса 101100 также сначала проверяем 1-й бит адреса. Поскольку он равен 1, то спускаемся вправо в префикс 10. Убеждаемся, что префикс 10 подходит. Далее проверяем 3-й бит адреса, спускаемся в узел 101, проверяем 4-й бит, и попадаем в узел 10111. Префикс 10111 не совпадает с адресом и последний посещенный нами белый префикс, как и в предыдущем случае, равен 10.

Сжатие уровня
Сжатие пути эффективно когда префиксов относительно немного. Когда же количество префиксов возрастает полезной становится другая оптимизация, называемая сжатие уровня (Level Compression). Здесь мы для некоторых узлов дерева храним не два дочерних элемента, а больше, например, 4, 8, 16 и т.д. В качестве индекса для выбора дочернего элемента используем сразу несколько битов в проверяемом адресе. Дерево использующее сжатие пути и уровня называется Level and Path Compressed Trie (LPC-Trie).


Рисунок 3. Использование Level and Path compressed trie для поиска наиболее длинного префикса для адреса 101100.

В данном случае узел 10 имеет не два дочерних узла, а четыре. Для выбора дочернего узла в адресе назначения проверяются сразу два бита: 3-й и 4-й, и используются в качестве индекса для выбора дочернего узла. Так, в адресе 101100 эти биты равны 11 (десятичное 3), т.е. после узла 10 спускаемся в дочерний узел с индексом 3, т.е. в узел 10111. Поскольку он не подходит, то наиболее длинный префикс равен 10.

Чем больше используется битов адреса назначения для нахождения очередного узла, тем меньше высота дерева и быстрее поиск. Однако при этом для некоторых индексов дочерние узлы отсутствуют и часть памяти тратится впустую.

Реализация в linux
Для проверки я брал ядро linux 4.4.60. В ядре linux для поиска лучшего префикса используется модифицированное LPC-Trie. При сжатии уровня используется максимальное количество битов, при котором количество отсутствующих дочерних узлов не превышает количество существующих дочерних узлов.

Модификация заключается в том, что все белые префиксы перемещаются в конечные узлы дерева. Промежуточные узлы используются исключительно для организации структуры дерева. В одном конечном узле может содержаться цепочка из нескольких префиксов, отличающихся друг от друга количеством нулей справа, т.е., например: 1, 10, 1000, как показано на рисунке:


Рисунок 4. Реализация trie в Linux для поиска наиболее длинного префикса для адреса 101100.

Поиск конечного узла производится при помощи LPC-алгоритма. Затем последовательно перебираются все префиксы в конечном узле, начиная с самого длинного. Ситуация при этом усложняется тем, что в некоторых случаях наилучший маршрут оказывается пропущенным при спуске по дереву и для его нахождения необходима проверка узлов слева (backtracking). Так, для адреса 101100 мы спускаемся к узлу 10111. Поскольку в нем нет нужного префикса, мы должны проверить узел 1010 (в нем может храниться префикс 101), а затем узел 1000 и в нем находим префикс 10. К счастью, необходимость в backtracking возникает, по-видимому не очень часто и не сильно снижает скорость поиска.

Для каких целей сделана данная модификация, усложняющая и замедляющая поиск, мне не совсем понятно, возможно она облегчает построение дерева при добавлении или удалении маршрутов, а может просто является унаследованной из какого-то алгоритма.

В linux есть возможность посмотреть дерево FIB. Для этого нужно ввести команду cat /proc/net/fib_trie. При этом выдается что-то похожее на:


Рисунок 5. Вывод команды cat /proc/net/fib_trie

Промежуточные узлы дерева обозначены символами +--, конечные узлы символами |--. В конечных узлах видны префиксы, упорядоченные по длине.

DIR-24-8
Можно ли еще ускорить поиск наилучшего префикса? Оказывается можно, если при этом пожертвовать памятью. Если предположить, что у нас есть сколько угодно памяти, то можно организовать поиск наилучшего префикса за единственное обращение к памяти. Для этого нужно создать большой массив, в ячейках которого хранить ссылки на наилучшие префиксы, а в качестве индекса при выборе ячейки использовать адрес назначения. Для нашего примера массив будет выглядеть следующим образом:


Рисунок 6. Массив для поиска наиболее длинного префикса за одно обращение.

Чтобы найти наилучший префикс для адреса 101100 мы просто смотрим в ячейку с индексом 101100 в двоичном виде, т.е. с индексом 44, и немедленно получаем, что наилучший префикс равен 10.

Для такого хранения префиксов нужен массив с количеством ячеек 2 в степени длины адреса назначения, т.е. для адресов длины 6 понадобилось 64 ячейки, а для реальных ip-адресов длины 32 понадобится более 4 млрд. ячеек.

Для сокращения количества ячеек заметим, что в нашем случае практически все префиксы равны длине 4 или меньше. Поэтому практически для всех адресов назначения достаточно посмотреть только первые четыре бита. Модернизированная схема поиска выглядит следующим образом:


Рисунок 7. Массивы для поиска наиболее длинного префикса за максимум два обращения.

В данном случае используются два массива. Сначала поиск осуществляется в левом массиве (из 16-ти ячеек), используя в качестве индекса первые четыре бита адреса назначения. Во всех случаях, когда адрес назначения не начинается с 1011 (одиннадцать в десятичном виде) мы сразу получаем наилучший префикс. Иначе мы смотрим последние два бита адреса и используем их в качестве индекса во второй таблице (из 4-х ячеек). Видно, что для адреса 101100 мы снова получаем наилучший префикс 10. Таким образом, при поиск наилучшего префикса в большинстве случаев выполняется за одно обращение, в худшем случае за два.

В случае реальных ip-адресов может использоваться аналогичная схема. Большинство префиксов в FIB являются длины 24 или меньше. Поэтому в качестве левого массива логично использовать массив, индексируемый старшими 24-мя битами адреса (16 777 216 ячеек). Для редких случаев, когда длина хранимых префиксов оказалась длиннее 24, используются дополнительные таблицы, индексируемые последними 8-ю битами префикса (256 ячеек для каждой дополнительной таблицы).

Данный быстрый способ поиска (максимум за два обращения) называется DIR-24-8 и используется, например, в DPDK.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/331944/


Метки:  

[Из песочницы] Бинарная сегментация изображений методом фиксации уровня (Level set metod)

Воскресенье, 09 Июля 2017 г. 13:39 + в цитатник

Метки:  

Признаки проблемного дизайна

Воскресенье, 09 Июля 2017 г. 13:15 + в цитатник
Понятие хорошего или плохого дизайна является относительным. В то же время есть некоторые устоявшиеся нормы программирования, которые в большинстве случаев гарантируют ему эффективность, сопровождаемость, тестируемость. Например, в объектно-ориентированных языках это использование инкапсуляции, наследования, полиморфизма. Есть набор шаблонов проектирования, которые в ряде случаев дают положительный эффект на дизайн приложения (а иногда и отрицательный, все зависит от ситуации). С другой стороны, есть противоположные нормы, следование которым иногда приводит к дизайну, который можно назвать проблемным. Такой дизайн как правило обладает следующими признаками (каким-то одним или несколькими одновременно):

  • Жесткость (трудно менять код, так как простое изменение затрагивает много мест);
  • Неподвижность (сложно разделить код на модули, которые можно использовать в других программах);
  • Вязкость (разрабатывать и/или тестировать код довольно тяжело);
  • Ненужная Сложность (в коде есть неиспользуемый функционал);
  • Ненужная Повторяемость (Copy/Paste);
  • Плохая Читабельность (трудно понять что код делает, трудно его поддерживать);
  • Хрупкость (легко поломать функционал даже небольшими изменениями);

Их нужно уметь понимать и различать для того, чтобы избежать проблемного дизайна либо предвидеть возможные последствия его использования. Эти признаки описаны в книге Роберта Мартина «Agile Principles, Patterns, And Practices in C#». Однако в ней, как и в некоторых других обзорных статьях по этой теме (которых, кстати, не очень много), приведено довольно краткое их описание и как правило отсутствуют примеры кода.

Попробуем исправить этот недостаток, более или менее подробно остановившись на каждом из этих признаков.

Жесткость


Как уже было обозначено выше, жесткий код бывает сложно менять, даже по мелочам. Это может и не быть проблемой в случае, если код меняется нечасто либо не меняется вообще. Таким образом, код получается вовсе даже неплохим. Но если он периодически требует изменений и делать это становится трудно, он становится проблемным. Даже если он работает.

Одним из распространенных случаев жесткости является явное указание типов классов вместо использования абстракций (интерфейсов, базовых классов и т.п.). Ниже приведен пример кода.

class A
{
  B _b;
  public A()
  {
    _b = new B();
  }

  public void Foo()
  {
    // Do some custom logic.
    _b.DoSomething();
    // Do some custom logic.
  }
}

class B
{
   public void DoSomething()
  {
    // Do something
   }
}

Здесь класс A жестко зависит от класса B. То есть если в будущем будет принято решение использовать вместо B другой класс, это потребует изменение класса A и, следовательно, его повторного тестирования. Кроме того, если от B жестко зависит множество других классов, ситуация может многократно осложниться.

Выходом из ситуации может быть введение абстракции, то есть некоего интерфейса IComponent и внедрение такой зависимости через конструктор (либо как-то еще) класса A. В этом случае класс A перестает зависеть от конкретного класса B, а зависит лишь от интерфейса IComponent. Класс B, в свою очередь, должен реализовывать интерфейс IComponent.

interface IComponent
{
  void DoSomething();
}

class A
{
  IComponent _component;
  public A(IComponent component)
  {
    _component = component;
  }

  void Foo()
  {
     // Do some custom logic.    
     _component.DoSomething();
     // Do some custom logic.
   }
}

class B : IComponent
{
  void DoSomething()
  {
    // Do something
  }
}

Давайте приведем более конкретный пример. Пусть есть некоторый набор классов, которые логируют информацию — ProductManager и Consumer. Их задача сохранить некоторый продукт в базе данных и заказать его соответственно. Оба класса логируют релевантные события. Пусть сначала логирование осуществлялось в файл. Для этого использовался класс FileLogger. При этом классы располагались в разных модулях (сборках).

// Module 1 (Client)
static void Main()
{
  var product = new Product("milk");
  var productManager = new ProductManager();
  productManager.AddProduct(product);
  var consumer = new Consumer();
  consumer.PurchaseProduct(product.Name);
}

// Module 2 (Business logic)
public class ProductManager
{
  private readonly FileLogger _logger = new FileLogger();
  public void AddProduct(Product product)
  {
    // Add the product to the database.
    _logger.Log("The product is added.");
  }
}

public class Consumer
{
  private readonly FileLogger _logger = new FileLogger();
  public void PurchaseProduct(string product)
  {
     // Purchase the product.
    _logger.Log("The product is purchased.");
  }
}

public class Product
{
  public string Name { get; private set; }
  public Product(string name)
  {
    Name = name;
  }
}

// Module 3 (Logger implementation)
public class FileLogger
{
  const string FileName = "log.txt";
  public void Log(string message)
  {
    // Write the message to the file.
  }
}

Если в начале было достаточно только файла, а затем возникает необходимость логировать и в другие хранилища, такие как база данных или облачный сервис сбора и хранения информации, то потребуется изменение всех классов в модуле бизнес логики (Module 2), использующих FileLogger. В конечном итоге это может оказаться сложным. Для решения этой проблемы можно ввести абстрактный интерфейс для работы с логгером, как показано ниже.

// Module 1 (Client)
static void Main()
{
  var logger = new FileLogger();
  var product = new Product("milk");
  var productManager = new ProductManager(logger);
  productManager.AddProduct(product);
  var consumer = new Consumer(logger);
  consumer.PurchaseProduct(product.Name);
}

// Module 2 (Business logic)
class ProductManager
{
  private readonly ILogger _logger;
  public ProductManager(ILogger logger)
  {
    _logger = logger;
  }
  
  public void AddProduct(Product product)
  {
    // Add the product to the database.
    _logger.Log("The product is added.");
  }
}

public class Consumer
{
  private readonly ILogger _logger;
  public Consumer(ILogger logger)
  {
    _logger = logger;
  }

  public void PurchaseProduct(string product)
  {
     // Purchase the product.
    _logger.Log("The product is purchased.");
  }
}

public class Product
{
  public string Name { get; private set; }
  public Product(string name)
  {
    Name = name;
  }
}

// Module 3 (interfaces)
public interface ILogger
{
  void Log(string message);
}

// Module 4 (Logger implementation)
public class FileLogger : ILogger
{
  const string FileName = "log.txt";
  public virtual void Log(string message)
  {
    // Write the message to the file.
  }
}

В этом случае при смене типа логгера достаточно поменять код клиента (Main), который инициализирует логгер и подкладывает его в конструктор ProductManager и Consumer. Таким образом мы закрыли классы бизнес логики от модификации в отношении типа логгера, что и требовалось сделать.

Кроме прямых ссылок на используемые классы, жесткость может проявляться любыми другими вариантами, приводящими к сложностям при изменении кода. Таковых может быть бесконечное множество, но попробуем привести еще один пример. Пусть есть некий код, который выводит на консоль площади геометрических фигур.

static void Main()
{
  var rectangle = new Rectangle() { W = 3, H = 5 };
  var circle = new Circle() { R = 7 };
  var shapes = new Shape[] { rectangle, circle  };
  ShapeHelper.ReportShapesSize(shapes);
}

class ShapeHelper
{
  private static double GetShapeArea(Shape shape)
  {
    if (shape is Rectangle)
    {
      return ((Rectangle)shape).W * ((Rectangle)shape).H;
    }
    if (shape is Circle)
    {
      return 2 * Math.PI * ((Circle)shape).R * ((Circle)shape).R;
    }
    throw new InvalidOperationException("Not supported shape");
  }

  public static void ReportShapesSize(Shape[] shapes)
  {
    foreach(Shape shape in shapes)
    {
       if (shape is Rectangle)
       {
         double area = GetShapeArea(shape); 
         Console.WriteLine($"Rectangle's area is {area}");
       }
       if (shape is Circle)
       {
         double area = GetShapeArea(shape); 
         Console.WriteLine($"Circle's area is {area}");
       }
    }
  }
}

public class Shape
{  }

public class Rectangle : Shape
{
  public double W { get; set; }
  public double H { get; set; }
}

public class Circle : Shape
{
  public double R { get; set; }
}

Из этого кода видно, что при добавлении новой фигуры придется менять методы класса ShapeHelper. Одним из вариантов может быть перенесение алгоритма отрисовки в сами классы геометрических фигур (Rectangle и Circle), как показано ниже. Этим мы изолируем релевантную логику в соответствующих классах, сузив тем самым ответственность класса ShapeHelper до вывода информации на консоль.

static void Main()
{
  var rectangle = new Rectangle() { W = 3, H = 5 };
  var circle = new Circle() { R = 7 };
  var shapes = new Shape[]() { rectangle, circle  };
  ShapeHelper.ReportShapesSize(shapes);
}

class ShapeHelper
{
  public static void ReportShapesSize(Shape[] shapes)
  {
    foreach(Shape shape in shapes)
    {
       shape.Report();
    }
  }
}

public abstract class Shape
{
  public abstract void Report();
}

public class Rectangle : Shape
{
  public double W { get; set; }
  public double H { get; set; }
  public override void Report()
  {
     double area = W * H;
     Console.WriteLine($"Rectangle's area is {area}");
  }
}

public class Circle : Shape
{
  public double R { get; set; }
  public override void Report()
  {
     double area = 2 * Math.PI * R * R;
     Console.WriteLine($"Circle's area is {area}");
  }
}

Как результат, мы фактически закрыли класс ShapeHelper для изменений по добавлению новых типов фигур за счет использования наследования и полиморфизма.

Неподвижность


Неподвижность проявляется в сложности разделения кода на переиспользуемые модули. В результате проект может потерять способность эволюционировать и как результат перестанет быть конкурентноспособным.

В качестве примера рассмотрим десктопную программу, весь код которой реализован в исполняемом файле приложения (exe) и был спроектирован таким образом, что вынесение бизнес логики в отдельные модули либо классы не предусмотрено. Затем, какое-то время спустя перед разработчиком программы возникли следующие бизнес требования:

  • Изменить пользовательский интерфейс, сделав его Web приложением;
  • Опубликовать функционал программы в виде набора Web сервисов, доступных сторонним клиентам для использования в их собственных приложениях.

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

На рисунке ниже показан пример неподвижного дизайна в противовес тому, который этим признаком не страдает. Они разделены пунктирной линией. Как видно из рисунка, распределение кода по повторно используемым модулям (Logic), а также публикация функционала на уровне Web сервисов позволяет использовать его в различных клиентских приложениях (App), что является несомненным плюсом.



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

Вязкость


Вязкость можно подразделить на два типа:

  • Вязкость разработки;
  • Вязкость окружения.

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

Вязкость окружения может проявляться в неэффективности процесса сборки, развертывания и тестирования приложения.

В качестве простого примера, где переплетаются оба типа вязкости, можно привести работу с константами, которые требуется (By Design) помещать в отдельный модуль (Module 1) для использования другими компонентами (Module 2 и Module 3).

// Module 1 (Constants)
static class Constants
{
  public const decimal MaxSalary = 100M;
  public const int MaxNumberOfProducts = 100;
}
 
// Finance Module
#using Module1
static class FinanceHelper
{
  public static bool ApproveSalary(decimal salary)
  {
    return salary <= Constants.MaxSalary;
  }
} 
 
// Marketing Module
#using Module1
class ProductManager
{
  public void MakeOrder()
  {
    int productsNumber = 0;
    while(productsNumber++ <= Constants.MaxNumberOfProducts)
    {
      // Purchase some product
    }
  }
}

Данный дизайн может оказаться вязким. Если по какой-то причине процесс сборки модуля констант занимает значительное время, разработчикам будет сложно ожидать его окончания (Вязкость окружения). Кроме того, можно обратить внимание на то, что в модуле констант содержатся достаточно разнородные сущности, относящиеся к принципиально разным частям бизнес логики (финансовый и маркетинговый модули). То есть модуль констант может меняться довольно часто по независимым друг от друга причинам, что может привести к дополнительным проблемам в виде процесса синхронизации изменений (Вязкость разработки).

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

Еще одним примером Вязкости Окружения можно считать разработку и тестирование приложения на удаленной виртуальной машине клиента. Иногда такой рабочий процесс становится невыносимым из-за медленного интернет соединения, в связи с чем разработчик может систематически пренебрегать интеграционным тестированием написанного кода, что в итоге может привести к проблемам на клиентской стороне при использовании этого функционала (Bugs).

Ненужная Сложность


В этом случае дизайн имеет неиспользуемый в данный момент функционал (так сказать на будущее, а вдруг пригодится). Этот факт может затруднять поддержку и сопровождение программы, а также увеличивать время разработки и тестирования. Например, рассмотрим программу, которая требует чтения некоторых данных из базы. Для этого был написан специальный компонент DataManager, который используется в другом компоненте.

class DataManager
{
  object[] GetData()
  {
    // Retrieve and return data
  }
}

Если разработчик добавит в DataManager новый метод для записи данных в базу (WriteData), который на данный момент не используется, но с очень малой вероятностью может пригодиться в будущем, это будет проявлением признака Ненужная Сложность. Зачем тратить время на написания потенциально неиспользуемого кода? QA иногда обязаны тестировать такой код, потому что он фактически публикуется и становится доступным для использования сторонними клиентами. Это тоже отъедает время релиза. Закладывать функционал на будущее стоит лишь при условии того, что возможная выгода от его наличия превысит затраты на его разработку и тестирование.

Ненужная Повторяемость


Наверное большинство разработчиков сталкивалиcь или столкнуться с этим признаком, который заключается в множественном копировании либо одной и той же логики, либо некоторого участка кода целиком. Основной угрозой при этом является уязвимость такого кода при внесении изменений — поправив что-то в одном месте, можно забыть сделать это в другом. Кроме того, требуется больше времени при внесении изменений по сравнению с ситуацией, когда код лишен этого признака.

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

Плохая Читабельность


Этот признак проявляется в том, что код трудно читать и понимать что он делает (и почему он делает именно так). Причинами плохой читабельности может быть несоблюдение требований к оформлению кода (синтаксис, именование переменных, классов и т.д.), запутанная логика реализации и другое.

Ниже приведен пример трудночитаемого кода, который реализует метод, оперирующий булевой переменной.

void Process_true_false(string trueorfalsevalue)
{
  if (trueorfalsevalue.ToString().Length == 4)
  {
    // That means trueorfalsevalue is probably "true". Do something here.
  }
  else if (trueorfalsevalue.ToString().Length == 5)
  {
    // That means trueorfalsevalue is probably "false". Do something here.
  }
  else
  {
    throw new Exception("not true of false. that's not nice. return.")
  }
}

Здесь можно выделить сразу несколько проблем. Во-первых, названия методов и переменных не подчиняются общепринятым конвенциям. Во-вторых, реализация метода не самая лучшая.

Возможно стоит сразу принимать булево значение, а не строку. Но даже если принимать строку, ее лучше сконвертировать в булеву величину в начале метода, а не пользоваться методом определения длины строки. В третьих, текст исключения (that's not nice) не соответствуют официально деловому стилю. Читая такие тексты, может сложиться ощущение, что код написан непрофессионалом (может быть спорный момент, но все же). Метод можно было бы переписать так (при условии, что он будет принимать булево значение, а не строку):

public void Process(bool value)
{
  if (value)
  {
    // Do something.
  }
  else
  {
    // Do something.
  }
}

Вот другой вариант рефакторинга (если принимать строку все же необходимо).

public void Process(string value)
{
  bool bValue = false;
  if (!bool.TryParse(value, out bValue))
  {
    throw new ArgumentException($"The {value} is not boolean");
  }  
  if (bValue)
  {
    // Do something.
  }
  else
  {
    // Do something.
  }
}

Над трудно читабельным кодом следует проводить рефакторинг, если он начинает приносить проблемы. Например в случае, если его сопровождение и размножение приводит к возникновению большого количества багов.

Хрупкость


Хрупкость программы означает простоту ее поломки в случае внесения изменений. Поломки бывают двух типов — ошибки компиляции и ошибки времени выполнения. Первые могут являться оборотной стороной Жесткости, вторые наиболее опасны, так как часто находятся уже на стороне клиента. Вот они то и являются показателем Хрупкости.

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

При этом часто ломается функционал, который даже не собирались менять. Он может пострадать из-за высокой связности логики различных компонентов между собой.

Рассмотрим конкретный пример. Здесь логика авторизации пользователя с некоторой ролью (определяется параметром roleId) по доступу к некоторому ресурсу (resourceUri) вынесена в статический метод.

static void Main()
{
  if (Helper.Authorize(1, "/pictures"))
  {
    Console.WriteLine("Authorized");
  }
}

class Helper
{
  public static bool Authorize(int roleId, string resourceUri)
  {
    if (roleId == 1 || roleId == 10)
    {
      if (resourceUri == "/pictures")
      {
        return true;
      }
    }

    if (roleId == 1 && roleId == 2 && resourceUri == "/admin")
    {
      return true;
    }

    return false;
  }
}

Можно заметить, что логика довольно «волосатая». Очевидно, что при добавлении новых ролей и ресурсов ее будет несложно поломать. В результате, некоторая роль может получить либо потерять доступ к некоторому ресурсу. Уменьшить хрупкость помогло бы создание класса Resource, который внутри себя хранил бы идентификатор ресурса и список поддерживаемых ролей, как показано ниже.

static void Main()
{
  var picturesResource = new Resource() { Uri = "/pictures" };
  picturesResource.AddRole(1);
  if (picturesResource.IsAvailable(1))
  {
    Console.WriteLine("Authorized");
  }
}

class Resource
{
  private List _roles = new List();
  public string Uri { get; set; }
  public void AddRole(int roleId)
  {
    _roles.Add(roleId);
  }
  public void RemoveRole(int roleId)
  {
    _roles.Remove(roleId);
  }
  public bool IsAvailable(int roleId)
  {
    return _roles.Contains(roleId);
  }
}

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

Что может помочь отловить ошибки времени выполнения? Ручное, автоматическое и юнит тестирование. Чем лучше организован процесс тестирования, тем больше защиты от того, что хрупкий код проявит себя на стороне клиента.

Часто хрупкость является оборотной стороной других признаков плохого дизайна, таких как Жесткость, Плохая Читабельность, Ненужная повторяемость.

Заключение


Мы попытались выделить и описать основные признаки проблемного дизайна. Некоторые их них взаимосвязаны. Нужно четко понимать, что проблемность дизайна не всегда неизбежно приводит к трудностям, она лишь указывает на вероятность их наступления. Чем меньше прослеживаются эти признаки, тем ниже эта вероятность.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/331732/


Метки:  

[Из песочницы] Уязвимость ВКонтакте: отправляем сообщение с кодом восстановления страницы на чужой номер

Воскресенье, 09 Июля 2017 г. 12:30 + в цитатник


Обычным весенним днем, занимаясь «подготовкой» к ЕГЭ по информатике, наткнулся на статью об уязвимости Facebook, позволявшей взломать все аккаунты в социальной сети, за которую выплатили 15000$. Суть уязвимости заключалась в переборе кодов восстановления на тестовом домене компании. Я подумал, а чем собственно ВКонтакте хуже? И решил попробовать провернуть подобный трюк у них. Зная, что веб-версия уже достаточно хорошо исследована, жертвой должен был стать Android клиент, а что из этого вышло можно прочитать под катом.

Смотрим трафик


Первым делом я захотел узнать, какую информацию приложение передает в сеть во время процесса восстановления страницы. Помощником в этом деле выступил Fiddler, я настроил его и Android устройство, как написано в официальной документации. Таким образом в Fiddler становятся доступны все HTTP/HTTPS запросы c устройства. Теперь, в приложении, смело выходим из аккаунта ВКонтакте и нажимаем на кнопку «Забыли пароль?». После ввода номера телефона приложение отправляет 2 HTTPS запроса. Особую ценность представляет второй, потому что именно он отвечает за отправку SMS с кодом восстановления.



Особое внимание стоит обратить на некоторые параметры запроса:
phone — номер на который отправляется SMS
session_id — рандомно генерирующаяся сессия операции восстановления
Попытка отправить запрос изменив его не увенчалась успехом. Мешает параметр «signature», который выступает в роли «подписи», как она генерируется разберемся немного позже.

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



Честно говоря на этом моменте мне хотелось начать перебирать коды восстановления, меняя значение параметра «code». К сожалению, и этот запрос защищен от изменения с помощью «signature». Придётся разобраться, как генерируется эта подпись.

Реверс инжиниринг: декомпиляция


Для первоначального анализа можно попробовать декомпилировать приложение ВКонтакте. Так можно получить некоторые части исходного кода на Java.

Как это сделать
1. Загрузить и распаковать dex2jar и jd-gui
2. Открыть apk приложения, как обычный архив, и «перетащить» .dex файлы на d2j-dex2jar.bat

Открываем в jd-gui все полученные .jar файлы. И не долго думая, делаем поиск по строке «signature».



Библиотека libverify за авторством Mail.Ru явно выпадает из общего списка найденного. Смотрим и не ошибаемся, формируемая строка очень похожа на url из предыдущих запросов.

localObject3 = String.format(Locale.US, "%s%s?%s&signature=%s", new Object[] { d(), e(), localObject3, URLEncoder.encode(ru.mail.libverify.utils.m.b(f() + (String)localObject4 + ru.mail.libverify.utils.m.c(a.b())), "UTF-8") });

Эта библиотека сделана в лучших традициях security through obscurity, весь код надежно обфусцирован. Поэтому, через jd-gui мне удалось узнать только то, что за «signature» прячется MD5-хэш от неизвестной строки.



Реверс инжиниринг: дизассемблирование


Мне требовалось узнать, что за строка поступает в функцию ru.mail.libverify.utils.m.b(). Самый простой способ сделать это — немного изменить код приложения. Ну что ж попробуем. Для начала используем apktool, с командой:

apktool.jar d vk.apk -r
(ключ -r для игнорирования ресурсов)

Теперь, в папках с smali-кодом находим файл в котором происходит генерация MD5. В моем случае путь был такой: smali_classes3\ru\mail\libverify\utils\m.smali. Переходим к нужному методу:

...
.method public static b(Ljava/lang/String;)Ljava/lang/String;
    .locals 8
    .param p0    # Ljava/lang/String;
        .annotation build Landroid/support/annotation/NonNull;
        .end annotation
    .end param

    :try_start_0
    const-string/jumbo v0, "UTF-8"

    invoke-virtual {p0, v0}, Ljava/lang/String;->getBytes(Ljava/lang/String;)[B
    :try_end_0
    .catch Ljava/io/UnsupportedEncodingException; {:try_start_0 .. :try_end_0} :catch_2

    move-result-object v0

    :try_start_1
    const-string/jumbo v1, "MD5"

    invoke-static {v1}, Ljava/security/MessageDigest;->getInstance(Ljava/lang/String;)Ljava/security/MessageDigest;

    move-result-object v1

    invoke-virtual {v1}, Ljava/security/MessageDigest;->reset()V

    invoke-virtual {v1, v0}, Ljava/security/MessageDigest;->update([B)V

    invoke-virtual {v1}, Ljava/security/MessageDigest;->digest()[B

    move-result-object v0
    ...

Строка, которую требовалось узнать передавалась в функцию в первом параметр-регистре (p0). Поэтому, чтобы получить ее, следует куда-нибудь вывести параметр, например, в Logcat. Добавляем в код несколько строк:

...
.method public static b(Ljava/lang/String;)Ljava/lang/String;
    .locals 8
    .param p0    # Ljava/lang/String;
        .annotation build Landroid/support/annotation/NonNull;
        .end annotation
    .end param

    # PATCH
    # String v0 = "vk-research";
    const-string/jumbo v0, "vk-research"
    # Log.d(v0, p0), где p0 параметр метода
    invoke-static {v0, p0}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I

    :try_start_0
    const-string/jumbo v0, "UTF-8"
    ...

После сохранения изменений собираем приложение с помощью apktool:

apktool.jar b vk -o newvk.apk

Теперь нужно подписать apk, я использовалAPK Signer.

После этого, предварительно удалив оригинальное приложение, можно установить и запустить наш измененный клиент ВКонтакте. Для получения logcat с устройства воспользуемся Android Debug Bridge. Подключаем Android-устройство по USB и последовательно выполняем команды:

adb devices
adb logcat

Как только присоединились к устройству и получили возможность смотреть логи, снова нажимаем на «Забыли пароль?» и вводим номер телефона. В окне adb появляется запись:



Становится понятно, что хэшируемая строка состоит из последовательно склеенных: части url, параметров запроса и еще одной строки-хэша (506e786f377863526a7558536c644968). И теперь, зная алгоритм генерации «signature» можем начать отправлять свои «подписанные» запросы.

Исследование


Для исследования я написал простую программу на C#, которая отправляла запрос на отправку SMS и делала попытки ввода кода. Воспользовавшись ей, я вводил случайные коды восстановления. Но ожидаемо уперся в лимит попыток:



Пытался повторно отправить смс, в надежде, что после этого пропадет лимит. К моему сожалению, сообщение приходило, но вводить код я все так же не мог:



Решил отправлять запросы с разными session_id, приходили смс с другими кодами восстановления, но я все еще упирался в лимит, теперь уже не в «ATTEMPTLIMIT», а в «RATELIMIT».



Про bruteforce
Наблюдая за приходящими SMS я заметил некоторую общую особенность кодов. Код восстановления состоял из 4 цифр (на момент написание статьи увеличили да 6) и одинаковые цифры не находились рядом. То есть всего ~6500 возможных вариантов кодов. Я подумал, что вполне возможно за 5 попыток угадать код, так например делали в Facebook. Но потом все же отложил эту затею.

Я пытался обойти лимиты всеми возможными способами. Менял IP-адреса, параметры запроса, номера телефонов, но сделать больше 5 попыток ввода кода у меня не получалось.

И тут, почти случайно, я решил отправить код восстановления на два разных номера, но используя один и тот же session_id. Моему удивлению не было придела, когда я увидел одинаковую SMS на обоих телефонах. Вот как это работало:




Из-за прокси нужна дополнительная проверка.

Таким образом получалась атака:

  1. Отправляем запрос на отправку SMS абоненту A с session_id C, ему приходит код 1234
  2. Отправляем запрос на отправку SMS абоненту B с session_id C, ему приходит код 1234
  3. Теперь, если абонент A знает номер телефона абонента B, он может восстановить его страницу. Восстанавливать можно было только ту страницу, на номер которой пришла последняя SMS с сходным session_id.

Вывод


Сразу после обнаружения уязвимости я написал репорт на HackerOne. Уже через 17 часов уязвимость была устранена. Спустя несколько дней мне выплатили 2000$. Данная уязвимость позволяла взломать большинство аккаунтов в социальной сети, в безопасности были только аккаунты с двухфакторной авторизацией (у них нельзя делать восстановление по номеру телефона). Репорт.

P.S. ЕГЭ по информатике сдал на 97 баллов
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/332684/


Метки:  

[Из песочницы] Oracle Data Integrator. SubstitutionAPI: Порядок выполнения подстановок. Часть 1

Воскресенье, 09 Июля 2017 г. 11:47 + в цитатник
Для кого эта статья
Статья предназначена для опытных разработчиков ODI (Oracle Data Integrator). Здесь рассмотрены плохо документированные аспекты связанные с порядком выполнения BeanShell-подстановок.

Подстановки %?$@


Спокойно! «%?$@» — это не эвфемизм, а спецсимволы, используемые для BeanShell-подстановок разных типов. Хотя именно в таким образом мог бы сказать о них разработчик, решивший использовать их все вместе, и не разобравшись в ряде тонкостей. А между тем — это чрезвычайно мощный инструмент, обеспечивающий гибкое формирование кода, выполняемого в сценариях.

Когда и где выполняются подстановки? Повторяются ли они в Target-коде для каждой source-строки? Можно ли вкладывать подстановки разных уровней? А одинаковых? Если я объявил Java-переменную, то где я всё еще могу её использовать? Почему иногда не работают функции Substitution API, а иногда работают (недокументировано в каком уровне подстановок какие функции применимы)? И так далее. Этим слабо документированным тонкостям и посвящается серия статей. И это первая из них.

Общим для любых подстановок является то, что внутри подстановки выполняется код на языке BeanShell Script. Обработка подстановок напоминает парсинг JSP или PHP. Аналогично различают два синтаксиса подстановок: сокращённый и полный. В сокращённом подстановка содержит только выражение, результат которого замещает эту подстановку при выполнении.


Полный синтаксис содержит полноценный BeanShell-код, который может что-то «напечатать» с помощью out.print(), или не делать этого вовсе, а делать другую полезную работу втихаря. В последнем случае после выполнения подстановки она просто изымается из кода не оставляя там никаких следов.


Уровни подстановок


Уровень %


Подстановки вида выполняются сразу же при старте сессии еще до того, причём физически эта подстановка выполняется на том хосте, который осуществляет запуск. То есть если вы запускаете ODI-сессию из дизайнера, то выполнение %-подстановок происходит на вашей рабочей станции, и если скрипт подстановки решит обратиться, например, к файловой системе (а в beanshell помимо всех возможностей Java есть еще и свои нативные функции для этого), то он увидит ваш локальный диск. Когда же запускается уже собранный ODI-сценарий — вызовом операции InvokeStartScen или это запуск из планировщика, то клиентом, инициирующим запуск, является сам ODI-агент: физическое место выполнения %-подстановки — сервер ODI-агента.

Если во время выполнения %-подстановки возникает исключение, то падения не происходит. Весь стек ошибок вываливается в output, становится результатом выполнения подстановки. Этот результат становится исходным кодом (уже невалидным), который попытается быть интерпретированным на следующем уровне, и падение сессии произойдёт уже там.

Эта подстановка не работает внутри шага Set Variable и Evaluate Variable. То есть она там воспринимается как обычный текст и никак не обрабатывается.

Из этого уровня нельзя подключиться ни к source- ни к target-соединению, а только к work-репозиторию. Т.е. odiRef.getJDBCConnection(«WORKREP») уже доступно, но ни эта функция с другими аргументами, ни getJDBCConnectionFromLSchema() не работают. Потому что никаких коннектов еще не существует: работа сессии еще не начата.

Уровень ?


Итак, сценарий уже находится в агенте и начал выполняться. Сессия создана. Для очередного шага (step) происходит финальное формирование кода: появляется запись в SNP_STEP_LOG, и формируется финальный код всех задач (tasks) этого шага. Вот тут и выполняются подстановки

https://habrahabr.ru/post/332682/


Метки:  

Понимание оракулов в блокчейне

Воскресенье, 09 Июля 2017 г. 06:51 + в цитатник
Перевод статьи Thomas Bertani из блога компании Oraclize
Этот пост дискуссия о том, чем на самом деле являются оракулы, так же мы расскажем о некоторых распространенных заблуждениях по этому вопросу.
Оракул — это третья сторона, вы общаетесь с оракулом когда вам нужны данные, которые вы не хотите (или не можете) извлекать самостоятельно. Причин для этого может быть много.
С одной стороны, вы можете не доверять отдельному объекту при подписании multi-signature транзакции Bitcoin. Например, вы хотите чтобы некоторые средства были перемещены только при определенных условиях. Вместо того, чтобы делать это самостоятельно (что не дает никаких гарантий внешним сторонам) или делегировать это третьей стороне (которой вы не хотите доверять, поскольку она может вести себя некорректно), вы разделяете процесс подтверждения транзакции различным сторонам (оракулам) через multi-signature транзакцию.
Путь с использованием N-of-M multi-signature транзакций заключается в том, что каждый оракул имеет только один закрытый ключ, и может поставить только одну подпись в тот момент когда он сочтет это нужным, но сама транзакция будет действительна одна и N-of-M оракулы будут иметь консенсус относительно того, какая транзакция должна пройти. Это намного правильней, чем доверять одной из внешних сторон, поскольку выбранные оракулы могут конкурировать и вы получаете низкую вероятность мошенничества.
Идея иметь распределенную oracles network существует уже несколько лет, однако найти консенсус по протоколу связи между оракулами (Orisi пыталась) сложно. Поиск сторон готовых присоединиться к oracles network, еще сложнее, так как необходим хороший процесс стимулирования, а так же простой и понятный дизайн с возможностью легко взаимодействовать и в этом пока консенсуса нет. Помимо всего этого существенным ограничением могут быть источники, которые вы хотите использовать для получения данных, так как некоторые из них могут быть недоступны без разрешения внешних сторон (подробнее об этом мы поговорим немного позже).
Если мы говорим о смарт-контрактах (Ethereum), то здесь совершенно по другому, логика подтверждения транзакций обеспечивается сетью через ваш собственный код смарт-контракта. Это означает, что оракул не ставит подпись после проверки некоторых условий, вместо этого он предоставляет вам данные, которые вы просили — условия могут быть проверены на вашей стороне напрямую, вы можете сами инициировать транзакцию или изменение статуса. Тем не менее вы не можете полагаться на распределенную сеть для получения внешних данных, так как приложения / сервисы, выполняемые в цепочках, живут в собственной замкнутой среде, и именно поэтому вам нужны оракулы для ввода внешних данных.
Растущие потребности в данных — это результат быстро растущих индустрий и соответственно находить данные, правильно их вытаскивать из реального мира становится все сложнее и сложнее. Однако, часто неправильное представление использования оракулов вызвано слабым пониманием того, какие данные вы хотели бы получать от оракулов.
Например, рынки предсказаний, такие как Augur или Gnosis, призваны дать хороший и надежный индикатор эволюционирующих настроений (или знаний) толпы вокруг фактов которые произойдут в будущем. Рынки предсказаний часто упоминаются как оракулы, но в более широком и совершенно другом смысле, чем мы обсуждали выше.
Еще один момент, который стоит обсудить, заключается в следующем: хотим ли мы назвать оракулом, только то что является например потоком котировок? Это всего лишь источник данных, и в большинстве случаев он не будет иметь никаких связей с блокченом. Финансовые учреждения часто считают «Bloomberg» или «Reuters» в качестве оракулов, но на самом деле используют их как источник данных. Быть оракулом, это значит взять на себя все сложности взаимодействия с блокчейном, а они не заинтересованы делать это, так как это требует дополнительных затрат и ресурсов. Тем не менее, оракул может предоставить доступ к данным Bloomberg, после того как выберет подходящий источник и правильную форму данных. Ссылаться на тех, кто представляется как «оракул» но на самом деле является «источником данных», это еще одно неправильное использованием этого термина.
Чтобы более глубоко понять это, мы можем определить 3 объекта:
  • источник данных
  • запрос
  • Оракул/Сеть оракулов
Что касается источника данных — это источник информации, которую вы ищете, это может быть что угодно в зависимости от вашего фактического запроса, например это может быть Augur (при рассмотрении будущих событий/фактов), “Bloomberg” (при поиске финансовых данных), «Bitcoin blockchain» (при поиске address balance, данных транзакции OP_RETURN или любых других данных блокчена), «WolframAlpha» (при поиске ответа на заданный запрос в Wolfram Alpha) или… “Web”? Получение веб-данных или данных с API является самым простым и распространенным местом, с которого вы можете получать данные.
Запрос — это формула, которую может понять выбранный вами источник данных, чтобы предоставить вам нужные данные.
Оракул/Сеть оракулов — это сторона которая отвечает за подключение к источнику данных
Здесь нужно обсудить два вопроса:
  1. Почему я должен доверять источнику данных? Большую часть времени вы не должны доверять. Найти консенсус различных источников данных — это хороший способ дать дополнительную надежность, при этом все еще используя «централизованные» данные
  2. Что, если Оракул/Сеть оракулов вернет мне неправильный результат? И это основной момент, предлагающий использовать консенсус оракулов вместо одного оракула. Но подождите, нет ли другого способа предотвратить возможность того, что бы оракул подделывал данные? Давайте поговорим об этом немного.

image
Подход Oraclize в качестве оракула отличается.
Вы можете рассматривать это как нечто среднее между оракулом и расширенной сетью оракулов. Oraclize потенциально может вернуть вам ответ, но не может изменять данные, поступающие из выбранного вами источника данных.
Вот почему мы относимся к сервису Oraclize как «доказуемо-честному». Это возможно благодаря TLSNotary/pagesigner. Это модификация TLS, которая используется службой oracle для предоставления криптографических доказательств, показывающих, что данные, которые вам предоставили, были действительно теми, которые данный сервер дал нам в определенное время. Помимо предоставления этого доказательства, Oraclize распространяет доказательство и через постоянную сеть Будучи также частью консорциума IPFS persistence consortium , наши доказательства имеют хорошие шансы быть доступными даже если наша система недоступна (по какой-либо причине).
Мы предоставляем инструменты для проверки нашей честности с помощью которых вы можете сделать эту проверку самостоятельно. Здесь, например, наш сетевой монитор для Ethereum который подключается на вашей, клиентской стороне (вот почему это так тяжело) с публичными нодами Ethereum и шлюзом IPFS (или с вами сами, если хотите), чтобы вы смогли находить транзакции Oraclize и доказать правдивость верифицировав их. При том вы можете легко контролировать сеть самостоятельно, чтобы гарантировать, что Oraclize ведет себя честно, и мы действительно хотим побудить вас сделать это!
image
Что это значит на практике? Если мы изменим ваши данные, любой может проверить это в любое время, автоматически и в одно мгновение и наша репутация будет потеряна. Если вам кажется, что наши ответы по-прежнему звучат слишком рискованно, вы, вероятно, захотите дождаться, когда мы выпустим релиз с открытым кодом для Ethereum, над которым мы работаем. Это означает, что ваш код смарт-контракта может получать доказательство в TLSNotary самостоятельно, и если данные недействительны то вы можете отбросить данные Oraclize.
По нашему мнению, сервис Oraclize является хорошим компромиссом для решения «проблемы оракулов» который сегодня работая таким образом подходит блокчейну и смарт-контрактам. Наличие сети оракулов поможет решить проблему «цензуры/простоя», но риски, связанные с использованием единственного оракула, резко сокращаются благодаря новому подходу Oraclize.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/332678/


Google в скором времени перестанет доверять всем сертификатам WoSign и StartCom

Суббота, 08 Июля 2017 г. 19:00 + в цитатник
imageСогласно сообщению от компании Google, в скором времени (а именно — начиная с выпуска Chrome 61, который ожидается в середине сентября) будет полностью прекращено доверие к сертификатам, выданным удостоверяющими центрами WoSign и StartCom. Речь ведётся о сертификатах, выданных до 21 октября 2016 года, срок действия которых ещё не истёк (более новые сертификаты были заблокированы в прошлом году).

В Chrome 57 (вышедшем в марте 2017 года) частично уже было прекращено доверие к старым сертификатом, но для сайтов, входящих в первый миллион по рейтингу Alexa, было сделано исключение. Теперь данный белый список будет убран и блокировка станет действовать в отношении выданных указанными СФ сертификатов к любому домену.

Стоит напомнить, что аналогичные меры уже действуют со стороны Mozilla: начиная с Firefox 51 (появившемся в января 2017 года) введено ограничение для новых сертификатов WoSign и StartCom, но поддержка сертификатов, выданных до 21 октября 2016 года, пока сохранена.

История началась с того, что в прошлом году представители Mozilla выявили ряд серьёзных нарушений в работе WoSign и StartCom. В ответ компания WoSign инициировала аудит, работу по устранению замечаний и повышению безопасности своей внутренней инфраструктуры, а также начала процесс получения новых корневых сертификатов. Из нарушений можно отметить:
  • Игнорирование предписания, регламентирующего деятельность удостоверяющих центров, запрещающего использовать при создании сертификатов алгоритм SHA-1 с 1 января 2016 года (WoSign выписывал сертификаты с SHA-1 задним числом);
  • Получение контроля за другим удостоверяющим центром (StartCom) без раскрытия сведений о совершённой сделке;
  • Халатное отношение к безопасности, в частности применение устаревших версий сетевых приложений, без надлежащей установки обновлений (используемый на DNS-сервере пакет Bind последний раз обновлялся в 2011 году и содержит 19 неисправленных уязвимостей);
  • Был зафиксирован инцидент, в результате которого была произведена выдача постороннему лицу сертификата для одного из доменов GitHub.

Трудно объяснить логику, которой руководствовались в WoSign, производя столь рискованные для репутации действия, однако прецедент налицо.
Используете ли Вы в настоящий момент сертификаты от WoSign и/или StartCom, и каковы ваши планы?

Проголосовало 24 человека. Воздержалось 11 человек.

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/332672/


Метки:  

Как запутать аналитика — 4. Вероятность и точность

Суббота, 08 Июля 2017 г. 16:23 + в цитатник
В прошлой статье я сказал, что числовые атрибуты напрямую связаны с операциями, которые мы проводим над объектами. При этом натуральные числа – самый простой из рассматриваемых нами атрибутов. Есть и более сложные. Например, матрицы. Если мы говорим о свойстве линейного преобразования в трехмерном пространстве, то оно записывается 9-ю числовыми значениями, из которых удобно сформировать матрицу размером 3 на 3. Причина этого в том, что два преобразования, выполненных последовательно, — тоже преобразование, числовые атрибуты которого могут быть получены путем перемножения двух матриц. В этом сила моделирования преобразования при помощи матрицы.

Я бы много отдал, чтобы преподавание математики строилось именно таким способом: через практическую задачу, через ввод нужных объектов (чисел, матриц, волновых функций) и объяснение, как операции над ними помогают решать конкретные задачи. Именно так строилось обучение в физмат школе, в которой мне довелось учиться – в интернате №18 при МГУ, спасибо преподавателям!

Итак, мы создали хранилище, куда можно поместить информацию о существительных, прилагательных и глаголах.

Мы разделили мухи и котлеты – моделирование объектов учета от их интерпретации. Все свелось к тому, что есть объекты учета (4-х мерные пространственные объемы), мереологические отношения между ними и трактовка этих отношений через классификацию. Единственное, что является для нас новым — это способ мышления о времени как о еще одном измерении. Например, на оси времени можно сделать отметки, которые позволяют привязаться к конкретным мгновениям и отмерить временные отрезки, моделируемые атрибутами «с:» и «по:».

Вероятность и точность


Представьте, что вас просят вспомнить, когда вы последний раз проходили флюорографию? Скорее всего, вы вспомните год, может месяц. У меня есть знакомая, которая вспомнит день, час и имя доктора, потому что у нее феноменальная память. Но я могу сказать лишь приблизительно месяц. Теперь представьте себе, что вы находитесь у доктора, который спрашивает вас о флюорографии, и у него есть информационная система, в которую он должен занести дату последнего прохождения вами флюорографии. Допустим, что вы вспомните, что это было в мае. Доктор, скорее всего, скажет: давайте для определенности запишем: 15-го мая. Ничего не напоминает? Возможно, вы сталкивались с такого рода ситуациями.

Когда проектировщик ИС указывает способ хранения данных об объекте учета, он указывает тип данных. Если речь идет о времени, то способы хранения информации о времени и временных интервалах, стандартизированы. Можно записать время с точностью до долей секунды. И это, порой, имеет значение. Однако, в примере с флюорографией я должен сказать так: май прошлого года плюс-минус месяц. Вы где-то видели формат записи данных о времени, чтобы можно было записать эту информацию именно так? Я видел такую возможность в BusinessStudio. Там можно сказать, что время прохождения флюорографии – 15 июня с дисперсией в один месяц и гауссовской формой распределения (чего, кстати?).

Другой пример: пусть вы создаете план работ на следующий год. И пусть вам известно, что работы по замене грозотроса начнутся в первой половине мая. Как записать в ИС эту информацию? Возможно, вы отдельно создадите план работ на месяц май, и укажите, что в мае надо начать менять грозотрос. Эта запись означает буквально следующее: начало работ по замене грозотроса определяется равномерным прямоугольным распределением шириной в месяц.

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

Например, говоря о будущем, мы можем сказать, что исполнителем операции по заключению договора будет кто-то из отдела продаж, но мы точно не знаем, кто именно. Если говорить о прошлом, то мы можем сказать, что исполнителем операции по продаже был кто-то из отдела продаж, но мы не знаем, кто именно. И тот и другой тезис моделируются одинаково – при помощи распределений.

Итак, на вопрос, что мы моделируем, можно ответить так: мы моделируем объекты учета, классифицируем их и делаем это с некоторой долей вероятности, если речь идет о будущем, или с некоторой точностью, если речь идет о прошлом.

Может показаться, что упоминание о точности и вероятности излишние, но на практике учет этих ньюансов оказывает существенное влияние на решение. Пример:
Пусть есть три события, измеренные с одинаковой точностью – плюс-минус один час. Пусть первое событие произошло где-то с 16-00 до 18-00, второе – где-то с 17-00 до 19-00, третье – где-то с 18-00 до 20-00. И пусть стоит задача выстраивания событий по порядку их свершения. Понятно, что есть 3 варианта: с вероятностью одна четвертая: 2, 1, 3, с вероятностью одна вторая: 1, 2, 3 и с вероятностью одна четвертая – 1, 3, 2.

Понятно, что принятие решения становится вероятностным. На первом курсе физтеха наиболее трудно проходит первая лаба, посвященная точности измерений и вероятности наших представлений. До студентов доходит очень туго, что школьная физика нам врет. В реальности нет точных ответов и решений.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/332668/


Метки:  

Применение LibVirt API, InfluxDB и Grafana для сбора и визуализации статистики выполнения VM

Суббота, 08 Июля 2017 г. 16:11 + в цитатник

В своей практике я достаточно много времени посвящаю проектированию и администрированию облачных инфраструктур различного назначения. В основном это Apache CloudStack. Данная система обладает отличными возможностями, но в части мониторинга, функциональности явно недостаточно (читайте — отсутствует), особенно, если на мониторинг смотреть шире чем мониторинг индивидуального объекта наблюдения (сервер, виртуальная машина).


В целом, в связи с более широкими требованиями к систем визуального анализа информации и потребностями в части интеграции с источниками данных стали распространяться специализированные решения для ad-hoc анализа данных, такие как Kibana, Grafana и иные. Данные системы могут интегрироваться со специализированными хранилищами временных рядов данных, одним из которых является InfluxDB. Статья расскажет о готовом решении, распространяемом в виде образа Docker, использующем LibVirt API, Grafana и InfluxDB, предназначенном для сбора и анализа параметров исполняющихся VM для гипервизора KVM.


Обзор решения


Решение представлено в форме Docker-контейнера, распространяемого по лицензии Apache License v2, поэтому оно может без ограничений применяться в любых организациях и изменяться, отражая потребности конкретной задачи. Контейнер размещается на выделенном сервере, python-утилита сбора данных удаленно подключается по протоколу TCP к LibVirt и отправляет данные в InfluxDB, откуда они могут быть запрошены с помощью Grafana для визуализации и анализа.


Контейнер доступен в виде исходных кодов на GitHub и в виде доступного для установки образа на DockerHub. Язык реализации — python.


Почему Docker-контейнер


Данное решение является конечным и удобным для внедрения, а так же не требует каких-либо дополнительных настроек и установки дополнительного ПО на серверах виртуализации, кроме разрешения доступа к API LibVirt по сети. Если доступ к API LibVirt снаружи не представляется возможным, то возможно установить Docker на хосте виртуализации и запускать контейнер локально.


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

Какие данные собираются


Сенсор собирает следующие данные о виртуальных машинах, доступные через LibVirt:


CPU:


    {
        "fields": {
            "cpuTime": 1070.75,
            "cpus": 4
        },
        "measurement": "cpuTime",
        "tags": {
            "vmHost": "qemu+tcp://root@10.252.1.33:16509/system",
            "vmId": "i-376-1733-VM",
            "vmUuid": "12805898-0fda-4fa6-9f18-fac64f673679"
        }
    }

RAM:


{
        "fields": {
            "maxmem": 4194304,
            "mem": 4194304,
            "rss": 1443428
        },
        "measurement": "rss",
        "tags": {
            "vmHost": "qemu+tcp://root@10.252.1.33:16509/system",
            "vmId": "i-376-1733-VM",
            "vmUuid": "12805898-0fda-4fa6-9f18-fac64f673679"
        }
    }

Статистика по каждому сетевому адаптеру с привязкой к MAC-адресу:


{
        "fields": {
            "readBytes": 111991494,
            "readDrops": 0,
            "readErrors": 0,
            "readPackets": 1453303,
            "writeBytes": 3067403974,
            "writeDrops": 0,
            "writeErrors": 0,
            "writePackets": 588124
        },
        "measurement": "networkInterface",
        "tags": {
            "mac": "06:f2:64:00:01:54",
            "vmHost": "qemu+tcp://root@10.252.1.33:16509/system",
            "vmId": "i-376-1733-VM",
            "vmUuid": "12805898-0fda-4fa6-9f18-fac64f673679"
        }
    }

Статистика по каждому диску:


{
        "fields": {
            "allocatedSpace": 890,
            "ioErrors": -1,
            "onDiskSpace": 890,
            "readBytes": 264512607744,
            "readIOPS": 16538654,
            "totalSpace": 1000,
            "writeBytes": 930057794560,
            "writeIOPS": 30476842
        },
        "measurement": "disk",
        "tags": {
            "image": "cc8121ef-2029-4f4f-826e-7c4f2c8a5563",
            "pool": "b13cb3c0-c84d-334c-9fc3-4826ae58d984",
            "vmHost": "qemu+tcp://root@10.252.1.33:16509/system",
            "vmId": "i-376-1733-VM",
            "vmUuid": "12805898-0fda-4fa6-9f18-fac64f673679"
        }
    }

Общая статистика по хосту виртуализации, как ее "видит" LibVirt:


    {
        "fields": {
            "freeMem": 80558,
            "idle": 120492574,
            "iowait": 39380,
            "kernel": 1198652,
            "totalMem": 128850,
            "user": 6416940
        },
        "measurement": "nodeInfo",
        "tags": {
            "vmHost": "qemu+tcp://root@10.252.1.33:16509/system"
        }
    }

Настройка LibVirt


В конфигурационном файле /etc/libvirt/libvirtd.conf необходимо установить:


listen_tls = 0
listen_tcp = 1
tcp_port = "16509"
auth_tcp = "none"
mdns_adv = 0

Внимание! Вышеуказанные настройки позволят соединяться с API LibVirt по TCP, настройте корректно файрвол для ограничения доступа.

После выполнения данных настроек LibVirt необходимо перезапустить.


sudo service libvirt-bin restart

InfluxDB


Установка (для Ubuntu)


Установка InfluxDB осуществляется по документации, например, для Ubuntu:


curl -sL https://repos.influxdata.com/influxdb.key | sudo apt-key add -
source /etc/lsb-release
echo "deb https://repos.influxdata.com/${DISTRIB_ID,,} ${DISTRIB_CODENAME} stable" | sudo tee /etc/apt/sources.list.d/influxdb.list

sudo apt-get update && sudo apt-get install influxdb
sudo service influxdb start

Настройка аутентификации


Выполним команду influx для открытия сессии к СУБД:


$ influx

Создадим администратора (он нам понадобится когда мы активируем аутентификацию):


CREATE USER admin WITH PASSWORD '' WITH ALL PRIVILEGES

Создадим базу данных pulsedb и обычного пользователя pulse с доступом к этой базе данных:


CREATE DATABASE pulsedb
CREATE USER pulse WITH PASSWORD ''
GRANT ALL ON pulsedb TO pulse

Активируем аутентификацию в конфигурационном файле /etc/influxdb/influxdb.conf:


auth-enabled = true

Перезапустим InfluxDB:


service influxdb restart

Если все сделано правильно, теперь при открытии сессии необходимо указывать имя пользователя и пароль:


influx -username pulse -password secret

Запуск контейнера для начала сбора данных


docker pull bwsw/cs-pulse-sensor

docker run --restart=always -d --name 10.252.1.11 \
             -e PAUSE=10 \
             -e INFLUX_HOST=influx \
             -e INFLUX_PORT=8086 \
             -e INFLUX_DB=pulsedb \
             -e INFLUX_USER=pulse \
             -e INFLUX_PASSWORD=secret \
             -e GATHER_HOST_STATS=true
             -e DEBUG=true \
             -e KVM_HOST=qemu+tcp://root@10.252.1.11:16509/system \
             bwsw/cs-pulse-sensor

Большинство параметров самоочевидны, поясню лишь два:


  1. PAUSE — интервал между запросом значений в секундах;
  2. GATHER_HOST_STATS — определяет собирать или нет дополнительно статистику по хосту;

После этого в журнале контейнера с помощью команды docker logs должна отражаться активность и не должны отражаться ошибки.


Если открыть сессию к InfluxDB, то в консоли можно выполнить команду и убедиться в наличии данных измерений:


influx -database pulsedb -username admin -password secret

> select * from cpuTime limit 1
name: cpuTime
time                cpuTime cpus vmHost                                   vmId          vmUuid
----                ------- ---- ------                                   ----          ------
1498262401173035067 1614.06 4    qemu+tcp://root@10.252.1.30:16509/system i-332-2954-VM 9c002f94-8d24-437e-8af3-a041523b916a

На этом основная часть статьи завершается, далее посмотрим каким образом с помощью Grafana можно работать с сохраняемыми временными рядами.


Установка и настройка Grafana (Ubuntu)


Устанавливаем, как описано в документации


wget https://s3-us-west-2.amazonaws.com/grafana-releases/release/grafana_4.4.1_amd64.deb
sudo apt-get install -y adduser libfontconfig
sudo dpkg -i grafana_4.4.1_amd64.deb
sudo service grafana-server start
sudo update-rc.d grafana-server defaults

Запускаем web-браузер и открываем административный интерфейс Grafana http://influx.host.com:3000/.


Добавление источника данных в Grafana


Детальная инструкция по добавлению источника данных на сайте проекта. В нашем же случае добавляемый источник данных может выглядеть следующим образом:



После сохранения источника данных, можно создать "дэшборды" и попробовать создавать запросы для графиков (поскольку данная инструкция не о том, как пользоваться Grafana, то привожу лишь выражения для запросов):


Загрузка CPU (минутки):


select NON_NEGATIVE_DERIVATIVE(MAX("cpuTime"), 1m) / LAST("cpus") / 60 * 100 from "cpuTime" where "vmUuid" = '6da0cdc9-d8ff-4b43-802c-0be01c6e0099' and $timeFilter group by time(1m)

Загрузка CPU (пятиминутки):


select NON_NEGATIVE_DERIVATIVE(MAX("cpuTime"), 5m) / LAST("cpus") / 300 * 100 from "cpuTime" where "vmUuid" = '6da0cdc9-d8ff-4b43-802c-0be01c6e0099' and $timeFilter group by time(5m)

Загрузка CPU (все VM):


select  NON_NEGATIVE_DERIVATIVE(MAX("cpuTime"),1m) / LAST("cpus") / 60 * 100 as CPU from "cpuTime" WHERE $timeFilter group by time(1m), vmUuid


Память VM (пятиминутная агрегация):


SELECT MAX("rss") FROM "rss" WHERE "vmUuid" = '6da0cdc9-d8ff-4b43-802c-0be01c6e0099' and $timeFilter GROUP BY time(5m) fill(null)

Статистика ReadBytes, WriteBytes для диска (пятиминутная агрегация):


select NON_NEGATIVE_DERIVATIVE(MAX("readBytes"),5m)  / 300 from "disk" where "image" = '999a1942-3e14-4d04-8123-391494a28198' and $timeFilter group by time(5m)

select NON_NEGATIVE_DERIVATIVE(MAX("writeBytes"),5m)  / 300 from "disk" where "image" = '999a1942-3e14-4d04-8123-391494a28198' and $timeFilter group by time(5m)

Статистика ReadBits, WriteBits для NIC (пятиминутная агрегация):


select NON_NEGATIVE_DERIVATIVE(MAX("readBytes"), 5m)  / 300 * 8 from "networkInterface" where "mac" = '06:07:70:00:01:10' and $timeFilter group by time(5m)

select NON_NEGATIVE_DERIVATIVE(MAX("writeBytes"), 5m)  / 300 * 8 from "networkInterface" where "mac" ='06:07:70:00:01:10' and $timeFilter group by time(5m)

Вся мощь языка запросов InfluxDB к вашим услугам, и Вы можете строить такие дэшборды, которые отвечают Вашим потребностям и позволяют производить наглядный анализ данных. Например, один из самых полезных для меня кейсов — это разбор инцидентов, бывает, что клиент жалуется на то, что "ваш код ****о" © и говорит, что его VM чудесно работала, а потом раз и все. Строим выражение, смотрим на картинку, видим как CPU его VM в течение часа уходил в пике и таки ушел. Скриншот — отличный аргумент при решении конфликта.


Еще можно анализировать самых интенсивно использующих различные ресурсы VM, чтобы мигрировать их на отдельные хосты. Да все, что угодно. В этом смысле Grafana, Kibana и подобные системы выгодно отличаются от традиционных систем мониторинга (например, Zabbix) тем, что позволяют делать анализ по требованию и строить комплексные аналитические наборы, а InfluxDB помогает обеспечить высокую производительность анализа даже на большом наборе данных.


Заключение


Код, получающий данные из LibVirt тестировался с VM, которые используют тома в QCOW2 формате. Я постарался учесть варианты LVM2 и RBD, но не тестировал. Если у кого-то получится протестировать код на других вариантах томов VM и прислать исправления для кода, буду признателен.


PS: При мониторинге сетевого трафика VM Вы можете обнаружить, что данные по PPS значительно меньше тех, которые Вы получаете посредством Sflow/Netflow на маршрутизаторе или tcpdump в VM. Это известное свойство KVM, сетевая подсистема которого не придерживается стандартного MTU в 1500 байт.


PPS: Документация по API LibVirt для python ужасна и мне пришлось продираться через разные версии, чтобы все же выяснить в каком виде возвращаются данные и что они означают.


PPS2: Если что, как говорят на Газорпазорпе, "Я рядом, если надо поговорить" ©

Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/332652/


Метки:  

Скрипт для экспресс-восстановления Excel-файлов после повреждения

Суббота, 08 Июля 2017 г. 14:23 + в цитатник
Данная заметка предназначен тем, у кого при попытке открыть Excel-файл выдается сообщение об ошибке вида


В моем случае с такой ошибкой открывался xlsx-файл (далее 1.xlsx), восстановленный с помощью R-Saver после вирусной атаки, подобной «Petya».

После распаковки содержимого файла 1.xlsx в папку "\1" через контекстное меню были выданы следующие ошибки


Оказалось, что эти служебные файлы имеют нулевой размер. Я проделал аналогичную процедуру с исправным файлом 2.xlsx и скопировал из его папки "\2" ненулевые файлы [Content_Types].xml и .rels поверх пустых из "\1". Далее добавил содержимое папки "\1" в архив .zip и переименовал его в 3.xlsx. В результате, файл 3.xlsx уже открылся с корректными данными хотя и с предупреждением


Для автоматизации проделанных выше процедур был разработан скрипт vbscript, распространяемый «As Is».
Исходный код скрипта ST1_XLSX_FIXER_v1
option explicit

Const THIS_SCRIPT_NAME = "ST1_XLSX_FIXER_v1.vbs"
Const SUBDIR_XLS_SRC = "ST1_XLSX_FIXER_DATA_v1"
Const SUBDIR_OUT = "ST1_XLSX_FIXED"
Const RES_SUFFIX = "_fixed_ST1_v1"

Dim fso: Set fso = CreateObject("Scripting.FileSystemObject")

'если запускаем скрипт автономно
if WScript.ScriptName = THIS_SCRIPT_NAME then
    if WScript.Arguments.Count > 0  then		
        Dim fname
        for each fname in WScript.Arguments
			if fso.GetExtensionName(fname) = "xls" then 
				WScript.Echo "Файлы формата Excel 2003 и ранее (.xls) не поддерживаются"			
			else
				FixCorruptedExcel fname			
			end if
		next
    else
        WScript.Echo "Для работы перенесите выбранные xlsx-файлы на скрипт"
    end if         
end if

Set fso = Nothing

Sub FixCorruptedExcel(fpath)
	
	Dim out_dir: out_dir = fso.GetParentFolderName(fpath) & "\" & SUBDIR_OUT
	if Trim(out_dir) <> "" then
		'создание папки результатов
		If not fso.FolderExists(out_dir) Then
			fso.CreateFolder(out_dir)
		end if
	End If

	'cоздать копию xlsx-файла с расширением .zip
	Dim extract_dir: extract_dir = out_dir & "\" & fso.GetBaseName(fpath)
	Dim fpath_zip: fpath_zip = extract_dir & ".zip"

	fso.CopyFile fpath, fpath_zip

	'выходной файл
	Dim fpath_fixed: fpath_fixed = extract_dir & RES_SUFFIX & ".xlsx"
	if fso.FileExists(fpath_fixed) then fso.DeleteFile fpath_fixed 
	
	'распаковка zip
	UnzipFile fpath_zip, extract_dir
	
	'удаление zip-файла
	fso.DeleteFile fpath_zip

	'восстановление битых файлов из папки
	Dim script_path: script_path = fso.GetParentFolderName(Wscript.ScriptFullName)
	fso.CopyFolder script_path & "\" & SUBDIR_XLS_SRC, extract_dir
	
	'создание zip	
	CreateEmptyZipFile fpath_zip
	
	'архивирование extract_dir
	Dim shell: set shell = CreateObject("Shell.Application")
	Dim extract_dir_obj: set extract_dir_obj = fso.GetFolder(extract_dir)
	shell.NameSpace(fpath_zip).CopyHere shell.NameSpace(extract_dir).Items      
	
	do until shell.namespace(fpath_zip).items.count = shell.namespace(extract_dir).items.count
		wscript.sleep 1000 
	loop

	'zip -> xlsx
	fso.MoveFile fpath_zip, fpath_fixed

	'удаление unzip-папки
	fso.DeleteFolder extract_dir, true

	WScript.Echo "Исправленный файл: " & vbCrLf & fpath_fixed
	Set shell = Nothing
	
end sub

sub UnzipFile(fpath_zip, extract_dir)

	'создание папки для распаковки
	If not fso.FolderExists(extract_dir) Then
		fso.CreateFolder(extract_dir)
	End If

	'извлечение xlsx - аналог операции контекстного меню "Распаковать в ..."
	Dim shell: set shell = CreateObject("Shell.Application")
	Dim sub_files: set sub_files = shell.NameSpace(fpath_zip).items
	
	Const FOF_SILENT = &H4&
	Const FOF_RENAMEONCOLLISION = &H8&
	Const FOF_NOCONFIRMATION = &H10&
	Const FOF_ALLOWUNDO = &H40&
	Const FOF_FILESONLY = &H80&
	Const FOF_SIMPLEPROGRESS = &H100&
	Const FOF_NOCONFIRMMKDIR = &H200&
	Const FOF_NOERRORUI = &H400&
	Const FOF_NOCOPYSECURITYATTRIBS = &H800&
	Const FOF_NORECURSION = &H1000&
	Const FOF_NO_CONNECTED_ELEMENTS = &H2000&

	Dim args: args = FOF_SILENT + FOF_NOCONFIRMATION + FOF_NOERRORUI
	shell.NameSpace(extract_dir).CopyHere sub_files, args
	
	Set shell = Nothing
	
end sub

sub CreateEmptyZipFile(fname)
	if fso.FileExists(fname) then
        WScript.Echo  "Файл " & fname & " уже существует", vbCritical, WScript.ScriptFullName
    end if
    
    Const ForWriting = 2

    Dim fp: set fp = fso.OpenTextFile(fname, ForWriting, True)
    fp.Write "PK" & Chr(5) & Chr(6) & String(18, Chr(0))
    fp.Close
end sub


Дополнительно к скрипту в архиве прилагается папка ST1_XLSX_FIXER_DATA_v1, где лежат эталонные файлы для замещения. Можно изменять ее содержимое в целях расширения области применимости скрипта на другие варианты битых файлов. Например, добавить туда обнаруженные вами варианты нулевых файлов.

Для работы скрипта необходимо:
  1. Скачать и распаковать архив ST1_XSLX_FIXER_v1.zip в любую папку
  2. Левой кнопкой мыши перенести один или несколько xlsx-файлов на скрипт ST1_XLSX_FIXER_v1.vbs
  3. Начнется процесс обработки каждого файла
  4. После успешной обработки каждого файла выдается сообщение вида



Принцип работы скрипта:
  1. Сохраняет входной файл неизменным
  2. Создает подпапку ST1_XLSX_FIXED
  3. Создает в ST1_XLSX_FIXED переименованную в zip копию xlsx
  4. Распаковывает zip в папку и копирует поверх нее ST1_XLSX_FIXER_DATA_v1
  5. Архивирует полученную папку в zip и переименовывает полученный файл в xlsx


Заключение
Данные эксперимент не претендует на общность использования, используйте предлагаемое решение на свой страх и риск. Со своей стороны планирую провести более широкий эксперимент и по результатам доработать скрипт. Текущее явное ограничение — скрипт не анализирует размер замещаемых файлов при копировании из ST1_XLSX_FIXER_DATA_v1, поэтому не умеет определять, какие именно служебные файлы оказались пустыми и требуют своей замены. Скорее всего, подобный способ применим, если утеряны именно служебные файлы, а не рабочие листы из "\1\xl\worksheets".

Также скрипт не подходит для файлов с расширением xls, созданных в версиях Excel 2003 и ранее, поскольку там используется другой формат хранения данных.
Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/332660/


Метки:  

Интервью в SD podCast с Павлом Одинцовым, автором FastNetMon, инструмента для обнаружения и отражения DDoS атак

Суббота, 08 Июля 2017 г. 13:30 + в цитатник

Метки:  

Libdispatch. Как сделать приложение отзывчивым

Суббота, 08 Июля 2017 г. 12:35 + в цитатник

image



Для повышения отзывчивости приложения необходимо грамотно разбивать выполнение задач на несколько потоков. Набор технологий в руках iOS-разработчика представляет из себя следующее. Способы представлены по возрастанию уровня абстракции.


  • Pthreads, или потоки POSIX. Библиотека для низкоуровневой работы с многопоточностью. Определена как набор типов и функций на языке C. Подробнее можно ознакомиться тут.
  • Background selectors. Это отправка сообщения объекту, которое будет исполнено на указанном потоке. В коде это селектор с названием performSelector и различными параметрами (например, performSelectorOnMainThread:withObject:waitUntilDone:). Документация
  • NSThread. Представлены как базовые средства для работы с потоками. Ознакомьтесь с концептуальным документом по работе с потоками и документацией.
  • Grand Central Dispatch. Библиотека, основанная на блоках — анонимных участках кода, иначе замыканиях. Рабочее название — libdispatch.
  • NSOperation. Построен на основе GCD. Стоит заметить, что сама операция является абстрактной сущностью и на практике стоит использовать NSInvocationOperation и NSBlockOperation.

В этой статье поговорим о вопросах CGD.


Libdispatch — это библиотека компании Apple, предназначенная для работы с многопоточностью. GCD впервые была представлена в Mac OS X 10.6. Исходные коды библиотеки libdispatch, реализующей сервисы GCD, были выпущены под лицензией Apache 10 сентября 2009. Также существуют версии библиотеки для других операционных систем семейства Unix, таких как FreeBSD и Linux. Для остальных пока поддержки нет. Правда есть неофициальные сборки libdispatch от пользователей.


Поговорим о внутреннем устройстве библиотеки. Сделаем предположение, на основе какой технологии она была разработана. Варианты: pthreads, background selectors, NSThread. Второй вариант однозначно не подходит — поскольку основу libdispatch составляет работа с блоками. Тогда из предположений остается NSThread или pthreads. А теперь рассмотрим поподробнее.


Устройство GCD


Заголовочные файлы «всея iOS»


Все началось с того, что был обнаружен сборник заголовочных файлов всех библиотек и протоколов в Obj-C для одной из самых последних версий операционной системы (на тот момент это была iOS 10). В проекте присутствуют публичные фреймворки — большинство тех, с которыми знакомы практически все разработчики, начиная от AVFoundation и заканчивая WebKit. К удивлению, даже в публичных фреймворках присутствуют такие свойства и методы, которые недоступны в оригинальной документации Apple. Например, свойство trustedTimestamp у объекта CLLocation.


Далее обнаруживается большой раздел приватных библиотек, например, PhysicsKit. К слову, есть интересный timeline жизни приватных фреймворков — рекомендую ознакомиться. Это стоит того, чтобы потратить несколько часов и поизучать интересные и частично вскрытые внутренности iOS (сильно не радуйтесь, там только сгенерированные заголовочные файлы). Оставшаяся часть отведена библиотекам и протоколам. Библиотек там не так много, да и именование у них похожие: lib + название. Например, libobjc или libxpc. А вот протоколов там настолько много, что даже github не отображает их все.


И да, среди всего прочего была обнаружена libdispatch. Как и для остальных библиотек в репозитории, для нее присутствуют только заголовочные файлы. Среди них намеков на устройство библиотеки нет. Сгенерированные заголовочные файлы для классов в большинстве случаев содержат несколько стандартных методов, среди которых: debugDescription, description, hash и superclass. В таком случае единственным вариантом остается исследование открытых исходников Apple.


Обзор открытого репозитория


Рассмотрим из чего состоит репозиторий libdispatch. Это исходники и заголовочные файлы нескольких уровней. Уровни включают в себя публичный (то, о чем вы привыкли думать как о libdispatch), внутренний и приватный уровень доступа. Стоит обратить внимание на документацию, которая предоставляется для утилиты командной строки. Среди всего прочего можно наткнуться на файлы конфигурации cmake и xcodeconfig, а также тесты в большом количестве.


Наиболее интересные места для нас:


  • обертка для Swift (так как стандартный dispatch никуда не делся и никто его не переделывал), которая находится в исходниках проекта
  • проект Xcode, в котором можно более удобно рассмотреть структуру и устройство библиотеки.

Репозиторий библиотеки активно поддерживается — регулярные коммиты от разработчиков несколько раз в месяц, которые активно чинят сломанную поддержку и компиляцию на различных платформах. Проект считается законченным, а некоторые проблемы, например, построение библиотеки на El Capitan, остаются нерешенными до сих пор.


Описание структуры проекта. Базовые объекты


Рассмотрим, что такое очередь в libdispatch. Очередь определена тремя макросами, определение можно найти в файле — queue_internal.h.


Определение очереди начинается с включения DISPATCH_STRUCT_HEADER — так сделано для всех объектов проекта. Этот общий заголовок состоит из определения OS_OBJECT_HEADER (сам OS_OBJECT_HEADER необходим для виртуальной таблицы операций — vtable и подсчета ссылок), нескольких полей, включая поле целевой очереди. Целевая очередь (target queue) представляется одной из базовых очередей — обычно очередью по умолчанию.


Далее очередь определяется макросами DISPATCH_QUEUE_HEADER и DISPATCH_QUEUE_CACHELINE_PADDING. Последнее нужно, чтобы убедиться, что структура оптимально поместится в линию кэша процессора. Макрос DISPATCH_QUEUE_HEADER служит для определение метаданных очереди, которые включают в себя «ширину» (количество потоков в пуле), номер для дебаггинга, обычный номер и список задач на выполнение.


Базовый тип для работы представлен как continuation. Он определен как включение единственного макроса DISPATCH_CONTINUATION_HEADER. В определение макроса входят указатель на таблицу операций, различные флаги, приоритет, указатели на контекст, функции, данные и следующую операцию.


Путем исследования приватных заголовочных файлов и исходников библиотеки было обнаружено, что libdispatch может быть скомпилирован используя библиотеку libpqw или POSIX Thread API.


Обзор программного интерфейса библиотеки libpqw


Итак, последняя версия GCD построена над оберткой над библиотекой pthread — libpwq, в состав разработчиков которых записана и компания Apple. Главная идея библиотеки состоит в добавлении уровня абстракции. Первая версия вышла в 2011 году, на данный момент последней стабильной является версия 0.9 от 2014 года.


Библиотека является прямой надстройкой над , внося новый уровень абстракции. Он включает в себя работу не с потоками, а с очередями задач: создание, установка приоритетов, добавление задач на исполнение. Например, добавление задачи осуществляется вызовом pthread_workqueue_additem_np, где передается очередь, указатель на функции для задачи и её аргументы.
Внутри библиотеки главной управляющей частью выступает некий менеджер, который оперирует очередями и списком задач. У менеджера всегда есть как минимум одна рабочая очередь. Очередь представлена обычной структурой с идентификатором, приоритетом (их существует всего три — высокий, низкий и приоритет по умолчанию), различными флагами и указателем на первую задачу. Задачи организованы в виде списка. Сама задача — структура с указателем на функцию, флагами, аргументами и указателем на следующую задачу, если таковая имеется.


Конечно, возможна компиляция libdispatch без библиотеки libpwq, тогда в таком случае будут использоваться pthreads. Это обусловлено тем, что он был анонсирован значительно раньше выхода этой библиотеки (в Mac OS X Snow Leopard в 2009 году).


Дерево вызовов от dispatch_async до создания потока или отправки элемента в очередь


Давайте для примера реализации рассмотрим какое-нибудь существующее решение в libdispatch. Возьмем всеми любимый вызов


DispatchQueue.main.async {
    // some asynchronous code...
}

На самом деле реализация тривиальна. Про саму swift'овую обертку будет рассказано чуть позже в данной статье. Скажем только, что CDispatch — скомпилированная библиотека GCD на С для Swift проекта.


public class DispatchQueue : DispatchObject {
    ...
}

public extension DispatchQueue {

    ...

    public class var main: DispatchQueue {
        return DispatchQueue(queue: _swift_dispatch_get_main_queue())
    }

    ...

    @available(OSX 10.10, iOS 8.0, *)
    public func async(execute workItem: DispatchWorkItem) {
        CDispatch.dispatch_async(self.__wrapped, workItem._block)
    }

    ...
}

В приведенном выше участке кода мы видим, как создается главная очередь и что из себя представляет асинхронный вызов кода. Разбор устройства GCD под капотом будет начинаться от всем известного dispatch_async.
Базовое дерево вызовов от момента запуска асинхронной задачи до момента создания потока (pthread_create) или же отправки задачи в более низкоуровневую библиотеку (lipbwq) будет следующим:


  • dispatch_async
  • _dispatch_continuation_async
  • _dispatch_continuation_async2
  • _dispatch_async_f2
  • _dispatch_continuation_push
  • макрос dx_push
  • _dispatch_queue_push
  • _dispatch_queue_push_inline
  • макрос dx_wakeup
  • _dispatch_queue_class_wakeup
  • _dispatch_queue_class_wakeup_with_override
  • _dispatch_queue_class_wakeup_with_override_slow
  • _dispatch_root_queue_push_override_stealer
  • _dispatch_root_queue_push_inline
  • _dispatch_global_queue_poke
  • _dispatch_global_queue_poke_slow
  • вызов pthread_create или pthread_workqueue_additem_np

Пройдемся по структуре наиболее интересных вызововов. Оригинальный метод dispatch_async:


void
dispatch_async(dispatch_queue_t dq, dispatch_block_t work)
{
    dispatch_continuation_t dc = _dispatch_continuation_alloc();
    uintptr_t dc_flags = DISPATCH_OBJ_CONSUME_BIT;

    _dispatch_continuation_init(dc, dq, work, 0, 0, dc_flags);
    _dispatch_continuation_async(dq, dc);
}

Что же здесь происходит? Во-первых, выделяется память на ранее определенный тип — continuation. Стоит напомнить принятую концепцию, согласно которой под типом тип_t понимается указатель на структуру тип_s. В таком случае, где-то в заголовочных файлах будет находиться определение (например, typedef struct dispatch_queue_s *dispatch_queue_t;). Во-вторых, устанавливаем флаги для инициализации данной структуры, которые передаются вместе с типом блока и очередью для исполнения инструкций блока. Например, четвертый параметр определяет приоритет, который по умолчанию устанавливается в 0.


Выделив память на структуру и проинициализировав ее, управление передается дальше в две функции (_dispatch_continuation_async и _dispatch_continuation_async2). Первая функция представляет собой невстраиваемую (noinline) заглушку для вызова другой уже встраиваемой (inline) функции, попутно разыменовывая флаги, и проверяя наличие барьера. Задача второй функции — выполнить соотвествующие проверки и отправить continuation на асинхронное выполнение в очередь. Под отправкой подразумевается использование функции _dispatch_continuation_push. Это происходит только в случае того, что очередь не переполнена или при отсутствии барьера.


В случае попадания в барьер, управление может передаться функции _dispatch_async_f2, где осуществляется проверка и устанавливается уровень QoS для continuation — иначе приоритет. Однако следующей все равно вызывается функция _dispatch_continuation_push, которая под собой вызывает макрос dx_push. Макрос разворачивается в довольно громоздкую конструкцию, а в конечном итоге это ведет к вызову функции _dispatch_queue_push_inline. Ее невстраиваемая обертка намеренно пропускается.


Главная причина, по которой так часто встраивают функции в одну невстраиваемую — уменьшение количества вызовов, но в тоже время управление сложностью кода. Таким образом достигается приемлемый баланс. Громоздкие конструкции с разыменованием указателей и их указателей легко помещаются в маленькие функции, которые уже потом компилятор встраивает в места вызова. Ну а разбираться с меньшим объемом данных с точки зрения человека всегда легче.


Функция _dispatch_queue_push_inline построена на большом количестве макросов. Среди наиболее интересных низкоуровневых конструкций (которые, кстати, используются по всему исходному коду libdispatch) можно отметить следующие:


  • функция atomic_load_explicit находится в стандартной библиотеке для атомарной работы и обеспечивает атомарное разыменование указателя. Любая логика указателей в проекте использует вызовы из заголовочного файла — ;
  • функции __builtin_expect() и __builtin_unreachable(), равно как и остальные вызовы __builtin-подобных конструкций имеют отношение к низкоуровневым оптимизациям для компилятора — к branch prediction.

Главная задача этой функции — выполнить проверку на переполнение очереди или на заблокированный барьер и передать управление. Далее управление попадает в функцию _dispatch_async_f_redirect и выполняется проверка был ли continuation перенаправлен в эту же очередь. В данной функции также происходит обновление начала и конца очереди — атомарная смена указателей.


Далее следует еще один макрос dx_wakeup — или вызов _dispatch_queue_class_wakeup. Это один из главных методов, в котором происходит обработка задач в очереди. Он проверяет условия барьеров, состояния очереди, а в случае несоблюдения условий, задача снова может повторно отправиться в очередь через уже известный dx_push.


В случае соблюдения условий задача передается в метод _dispatch_queue_class_wakeup_with_override, который является оберткой над _dispatch_queue_class_wakeup_with_override_slow с проверкой на изменение приоритетов задачи и возможностью их перезаписи. Наличие slow в названии соотносится с механизмом встраивания функций — логика разбивается на несколько функций с целью упрощения ее поддержки.


Далее следуют вызовы, которые непосредственно обрабатывают задачи в цикле и содержат в себе большое количество логики по взаимодействию и настройке указателей, проверке различных флагов и т. п. В процессе выполнения последним вызовом становится pthread_create или pthread_workqueue_additem_np. Конечно, если диспатч будет построен без использования libpwq, то в дело вступит внутренний менеджер управления потоками, а принцип его работы похож на описанный выше.


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


Пишем на Swift


А теперь давайте вкратце посмотрим, в чем особенности промежуточной swift-библиотеки, которая непосредственно взаимодействует с libdispatch. Как известно, появилась она с третьей версии Swift. Представляет из себя обертку над оригинальным libdispatch c добавлением приятных swift'овых перечислений и вынесением функциональности в расширения. Все это, конечно же, входит в главную задачу библиотеки — предоставление удобного API для работы с GCD.


Начнем с файла, в котором громоздкие типы libdispatch данных превращаются в элегантные классы Swift — Wrapper.swift. Этот файл может служить отображением всего проекта.


Общий подход состоит в том, что создаются несложные оболочки для большинства объектов. Объекты оригинального libdispatch, такие как dispatch_group_t или dispatch_queue_t, хранятся в объектах-обертках в свойстве __wrapped. Большинство функций делают один-единственный вызов непосредственно функций оригинального libdispatch над свойствами __wrapped.


Рассмотрим простенький пример:


public class DispatchQueue : DispatchObject {

    // объект для работы с libdispatch
    internal let __wrapped:dispatch_queue_t

    ...

    final internal override func wrapped() -> dispatch_object_t {
        return unsafeBitCast(__wrapped, to: dispatch_object_t.self)
    }

    ...

    public func sync(execute workItem: ()->()) {
        // вызов функции с одноименным названием
        dispatch_sync(self.__wrapped, workItem)
    }

    ...
}

С другой стороны, существуют и вызовы, которые не состоят из одной строчки. В них происходит приведение типов, подсчет промежуточных значений, проверка на версию системы и вызов соответствующих методов. Стоит также упомянуть, что в файле Private.swift происходит запрет прямых вызовов методов библиотеки libdispatch. Пример приведен ниже. Поэтому Вы никак уже не сможете писать менее swift'овый код (кроме, конечно старых версий свифта или собранной самостоятельно библиотеки libdispatch).


@available(*, unavailable, renamed:"DispatchQueue.async(self:group:qos:flags:execute:)")
public func dispatch_group_async(_ group: DispatchGroup, _ queue: DispatchQueue, _ block: @escaping () -> Void)
{
    fatalError()
}

Предупрежден — значит, вооружен


Итого, получилось описание о принципах работы libdispatch. Предположения относительно ее внутреннего устройства подтвердились. libdispatch действительно построен на POSIX Thread API — как на самом минимальном API для обеспечения работы с многопоточностью.
Последняя версия libdispatch использует другую библиотеку (libpwq), но суть остается та же.


И вот у вас возник вопрос — а зачем вообще понимать что там на низком уровне? Понимание низкоуровневых вещей аналогично знанию базовых концепций. С их помощью вы не сделаете что-то быстро, но будете избегать глупых ошибок в будущем.


Понимание и знание низкоуровневых вещей позволит решать нетривиальные задачи в данной области. Если вам придется писать что-то на pthread для iOS, теперь уже будете подготовленным.


Ссылки


Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/332026/


Метки:  

Личный опыт: как ИТ-специалисту переехать на работу в США, надеясь только на себя

Суббота, 08 Июля 2017 г. 10:34 + в цитатник

Метки:  

Moneyball на бирже: как новые технологии меняют не только трейдинг, но и работу хедж-фондов

Суббота, 08 Июля 2017 г. 10:15 + в цитатник


В 2003 году был опубликован бестселлер Майкла Льюиса под названием «Человек, который изменил все» (“Moneyball”) — это биографическая спортивная драма, рассказывающая историю генерального менеджера бейсбольной команды «Окленд Атлетикс» Билли Бина. Ему удалось добиться впечатляющих успехов с помощью анализа данных при формировании состава.

Бин смог эксплуатировать неэффективности, существовавшие на рынке игроков в бейсбол — он подбирал членов команды не на основе «шестого чувства», на которое опирались большинство скаутов команд, а с помощью статистического подхода. Это позволило ему при относительно небольшом бюджете показать лучший результат, чем большинство более богатых команд.

Этот же подход может быть применен к инвестированию и торговле на бирже — данные помогают обнаруживать и использовать существующие на финансовом рынке неэффективности, пишут финансисты Джон Гилчрист и Грант Уотсон на страницах издания Business Live.

Новая эра в работе хедж-фондов


Такой «количественный» подход уходит корнями в тридцатые годы прошлого века, однако объём средств, под управлением финансистов, придерживающихся его, до недавних пор происходил достаточно медленно. Однако в последние восемь лет количественные (или квантовые — от англ. Quantitative, “quant”) хедж-фонды получили в управление почти $1 трлн, это почти треть от всего объёма активов всех мировых хедж-фондов.

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

Количественные фонды постоянно ищут уникальные методологии, составляют наборы данных и разрабатывают торговые стратегии. В этом им помогают технологии больших данных, машинного обучения и искусственного интеллекта (ИИ), объединенные со сверхбыстрыми компьютерами.

В докладе Deloitte Investment Management Outlook 2017 большие данные, машинное обучение и ИИ названы главными трендами текущего года.

Даже традиционные управляющие начинают внедрять подходы количественного инвестирования. Например, фонд Tudor Investment Corporation был вынужден сократить 15% персонала из-за низких результатов и последующего отзыва средств со стороны инвесторов — это заставило знаменитого управляющего Пола Тудора-Джонса (Paul Tudor-Jones) провести организационные изменения для того, чтобы софкусироваться на более технологических подходах, основанных на работе с данными. «Ни один человек не может победить машину, и ни одна машина не победит человека с машиной», — так он, по слухам, обосновал изменения в работе фонда оставшимся сотрудникам.

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

Как это работает


Количественный подход позволяют избежать ошибок при принятии инвестиционных решений, которые могут быть основаны на поведенческой предвзятости людей, управляющих активами. При его применении хедж-фонды используют системы, которые анализируют множество фундаментальных факторов и выявляют те из них, которые коррелируют с хорошими или плохими результатами инвестиций в конкретные активы. Эти фундаментальные факторы часто лежат в основе рыночных трендов, а их анализ позволяет видеть эти тренды на начальном этапе и динамически адаптировать портфолио активов, например, каждый месяц. Таким образом, повышается вероятность остаться в плюсе при любом развитии событий на рынке.

Также постоянно осуществляется поиск новых статистически значимых факторов, а те из них, которые перестают таковыми быть, удаляются из использующихся моделей расчетов. Это позволяет подстраиваться под изменение рыночной коньюнктуры. К примеру, фонд Old Mutual Customised Solutions добавил в свою модель принятия решений факторы, описывающие ситуацию с окружающей средой, настроениями в обществе и активность регулирующих органов.

Для отслеживания настроений могут использоваться специальные системы анализа новостей — это помогает понять вероятное направление действий «толпы», то есть множества частных игроков финансового рынка. Таким образом, хедж-фонды играют на неэффективности финансового рынка, возникающих из-за поведенческих предвзятостей его участников.

Другие материалы по теме финансов и фондового рынка от ITinvest:


Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/332520/


Метки:  

Чем занимались пиарщики РПЦ в день встречи Путина и Трампа на G20?

Суббота, 08 Июля 2017 г. 09:41 + в цитатник


В рамках данной стати мы будем проводить замеры динамики пульсации сущностей в информационном пространстве.

Выборка


За сегмент репрезентативной выборки информационного пространства мы приняли топ 1,000,000 доменов мира по Алексе и заставили краулер обходить главные страницы этих сайтов ежедневно.

Данные


Полученные данные обсчитываем на предмет упоминания в текстах интересующих нас сущностей (в данном случае: РПЦ, Путин, Трамп, G20) и отрисовываем на графике.

Предыстория


30 июня мы добавили поиск упоминания саммита G20, было интересно, как информационное пространство реагирует на достаточно крупное событие в мире политики. Ожидалась первая встреча президентов, всё обещало быть интересным. Путина и Трампа отслеживали и раньше, их удобно использовать в качестве сравнительных величин при анализе неизвестных сущностей, ну а РПЦ как-то добавили из спортивного интереса, хотелось понять, что происходит там вообще.

Визуализация


Вот так выглядит график роста упоминаний G20:



Интерактивный график

К моменту наступления события количество упоминаний выросло с 10 до 40 тысяч, это действительно большая информационная волна, для масштаба, можно сравнить с Российскими политиками:



Интерактивный график

ТОП 1 Медведев — 2,2к упоминаний, правда, это на русском, но все же. Заметно как Поклонская рванула вверх 6-го числа (в тот день она выложила нарезку порно)

Давайте посмотрим на всплеск упоминаний Путина и Трампа в день встречи:



Интерактивный график

Как мы можем заметить, для Путина — это самый сильный всплеск за последнее время (на латинице, сравните с волной по прямой линии за 15-е число), для Трампа, в принципе, тоже.

Если рассмотреть упоминания Путина отдельно (на кириллице и латинице), то картинка становится интереснее:



Интерактивный график

Обратите внимание на 15-е число, прямая линия, количество упоминаний Путина на кириллице практически сравнялось с показателем на латинице, огромный всплеск. Можно заметить приоритеты русскоязычного пространства, его явно больше интересуют внутренние дела, заметно, как с приближением G20 плавно увеличиваются упоминания Путина на русском, совершенно иное поведение в англоязычном сегменте.

Причём тут пиарщики РПЦ?


Если присмотреться к графику упоминаний аббревиатуры РПЦ:



Интерактивный график

Можно заметить, как Русская Православная Церковь стабильно наращивает свое присутствие в интернете, хайпят. Естественным ли образом или пиаром — вопрос отдельного исследования, но нам стало интересно, почему индекс упоминаний РПЦ обвалился в день встречи президентов и мы посмотрели на список доменов, с которых пропала аббревиатура. Выяснилось, что это на 87% те же, кто помогал усиленно освещать G20 в русском сегменте. Может в одном офисе сидят?)

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

Данные на графиках обновляются ежедневно.
Хотели бы вы замерить в данном корпусе что-нибудь своё, прямо регуляркой?

Проголосовал 1 человек. Воздержавшихся нет.

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

Original source: habrahabr.ru (comments, light).

https://habrahabr.ru/post/332656/


Метки:  

Поиск сообщений в rss_rss_hh_new
Страницы: 1437 ... 1042 1041 [1040] 1039 1038 ..
.. 1 Календарь