TabHide 之後的分頁群組插件調查

Firefox 59.0a1 之後新增了大家等待已久的 TabHide 功能,所以我就來重新調查一次有什麼插件是符合我需求的了。我的希望是儘量能夠逼近 Quicksaver 原版 Tab Groups 的功能。

Conex

Conex 好像是 addons.mozilla.org 上面蠻熱門的類 Tab groups 插件,但我實測之後發現:

  1. 他的每一個分頁群組會存在一個 contextualIdentities 容器 (container) 裡面,這不合我的用途,因為
    1. 我已經把我的不同用途的 cookie store 放到不同的 Firefox 的 profile 裡面了,我希望我的所有分頁群組共用一個 cookie store。
    2. 這行為和本來 Quicksaver 的版本不一樣。
    3. 而且 contextualIdentities 我之前檢閱完之後就覺得是個設計、模組化不良的 API
  2. 似乎是因為用了 containers 的關係,建立新容器、改名容器需要到 Firefox 的設定頁面裡面手動改。

Simple Tab Groups

  1. 切換不同群組的時候,不在顯示中的群組裡面的分頁會被卸載

後來我研究了一下,發現這個插件切換分頁群組的功能完全是使用 tabs.remove() 來達成的,也就是切換的時候會把分頁關掉,換回來的時候再打開。目前他們有個 issue 是要實作切換群組的時候分頁不會被卸載

Panorama View

這個插件目前最接近我的需求,不同分頁群組使用同一個 cookie store 和 Quicksaver 的版本相比,缺少幾個功能:

  1. 清單(專屬的清單頁面或是 context menu )顯示分頁群組和群組中的分頁:正在開發中
  2. 在不同群組之間顯示一樣的釘選分頁:有 PR

另外幾個是我覺得可以加的功能,我有空研究的話可以加加看:

  1. 指定不同分頁群組的顏色,這樣會達到類似 containers 的顯示效果,但是仍然共用 cookie store 。可能可以重複利用 VivaldiFox 這個插件的程式碼來作出不同顏色。

其他

另外,閒逛的時候還發現了幾個有趣的插件:

  • Social Fixer: 各種 Facebook 延伸功能,可以過濾貼文、標示貼文為已讀、移除特定區塊等等。但可惜不是開源的。
  • Auto Tab Discard : 簡單的定時 unload 分頁套件,我在 57 前就有用其他類似的插件。

更新 2/10

我發現 Simple Tab Groups 雖然缺少最重要的功能,但是程式庫、國際化和 UI 比 Panorama View 完整。

 

OpenVPN server settings in Asuswrt-Merlin

By default username/password authentication doesn’t seem to be enabled, choose Advanced Settings in the VPN server tab:

Down below, the Cipher Negotiation default will need to be disabled to work with OpenVPN clients <2.4.0 , which are quite common (Ubuntu 16.04 LTS still has OpenVPN 2.3.10 in package repo).

By default the VPN server also does not push routes, i.e. connection will be established but the client won’t change their default route to the tunnel interface created. This action could be added in the client ovpn file redirect-gateway def1, or the settings may be “pushed" by the server to the client by adding this server directive: push "redirect-gateway def1"

Update: I didn’t notice before, the “Direct clients to redirect Internet traffic” directive seems to do just that.

Firefox 57 毀掉分頁群組之後怎麼辦

眾所周知,預計 2017 年 11 月釋出的 Firefox 57 將移除所有舊版附加元件(使用 XUL/XPCOM/Addon SDK 的附加元件)的支援,只支援 WebExtensions 的附加元件。

如此重大的修改將會讓我失去 Firefox 對我來說最重要的分頁群組功能,原本分頁群組功能的開發者 Quicksaver 已經表示不會將原本的 Tab-Groups 插件移植到 WebExtensions API ,等於是宣判了這插件的死刑。

除了分頁群組之外,過去所有使用者熱愛的附加元件都將停止運作,各大討論版上面使用者哀嚎四起,還有 change.org 的連署,可惜沒過。

Mozilla 這個決定蠢斃了:

  • 大部分的使用者不使用附加元件沒錯,但不支援 XUL 附加元件會讓一大群 power user 和死忠粉絲背棄
  • 畢竟 Firefox 雖然比 Chrome 慢,但也只有 Firefox 可以讓你客製化到都認不出來,像我使用的 Firefox-on-OS-X Label 附加元件。 Chrome 你要做這 API ?還要先重新定義一個 Javascript 的界面、新增 API Permission 、一堆重複的工。Firefox?登登!Components.interfaces.nsIMacDockSupport !附加元件直接 tap into 原本就是負責處理這東西的內部界面,不需要重新定義界面巴拉巴拉。附加元件 15 行寫完收工。

好吧抱怨就此結束 :-p 我比較喜歡 Firefox 這樣可以自己自由 mod 的舊架構,而不是開發者爽開給你 API 才能 mod 的新架構。

身為一個分頁群組難民兼任自己要的功能自己寫的開發者,當然想要調查一下有什麼可行的解決方案可以拯救分頁群組功能,我整理出這些方法:

1. build 自己的 firefox fork ,把 legacy extension 直接包進去讓它可以用 legacy API

這會有點麻煩,每次釋出新版的時候要自己 patch 加入想要的 extension ,每個人想要的 extension 可能也不一樣,但這是保留原汁原味 legacy extension 唯二的方法。

挖了一下 Firefox 的 source code,原本內建在 Firefox 的 Panorama (就是分頁群組功能)在 codebase 裡面的這裡(應該是啦我有點不確定):mozilla-esr38/browser/components/tabview ,後來 tabview 被獨立出去成為 tabgroups extension ,所以理論上我只要把這個過程逆向操作,我就可以把現有的 extension 包回去 firefox 。但這 code structure 跟後來獨立出來成為 extension 的 tabgroups 長相實在差有點多,讓我有點搞不清楚到底當時是怎麼整理 codebase 獨立成 extension 的,這還需要更仔細的研究。

這麼做也有極限,因為 legacy API 和 extension 都不會再被維護,所以如果有新的 codebase 影響 legacy API 的運作,也不會被修正,legacy extension 也不會為了 broken API 去 workaround ,所以在更久的未來可能終究無法正常使用。

2. 啟動 legacy API 相容設定

猜測在 57 應該會有個 about:config 設定值可以允許 legacy extension 被安裝並且使用 legacy API 才是,雖然我還沒仔細去看這個設定值在哪裡。(等我找到再來更新這一段)

這個和方法1也有一樣的問題, legacy API 和 extension 都不會再被維護。

3. 使用 /firefox-tabgroups

這個 extension 本來是 tabgroups 的替代方案,也使用 legacy API, 但是和 tabgroups 不一樣的地方是作者 denschub 承諾說會支援新的 firefox ,雖然怎麼做、會有什麼功能他沒有說,但勢必一樣得受限於新的 WebExtensions API 。

4. 使用 contextualIdentities API 的相關插件

Firefox 前陣子新推出了一個 containers testpilot,基本上的概念是「在 Firefox 中透過不同容器來管理不同的線上身分,不論是線上購物、規劃旅遊行程、工作使用,更可以建立其他不同色彩與名稱的自訂容器。每個容器當中的 Cookie 會分開儲存」,它的目標使用情境看起來和分頁群組有些相關,因為分頁群組確實偶爾會被拿來這樣使用,但又有相當不同,containers testpilot 原本不是設計給「分頁管理」的情境下來使用的,所以沒有提供「顯示/隱藏」特定分頁的 API 。

有人去 containers 的 github 開了一個 issue 說,可不可以接受 containers 作為分頁管理工具的用途,獲得超多回應,畢竟現在這看起來是最有希望帶回分頁群組功能的道路了。

基於 contextualIdentities API  的插件目前有 eoger/tabcenter-reduxjonathanKingston/sea-containers ,這兩個是一開始就以 WebExtensions API 開發的插件,另外未來 denschub/firefox-tabgroups 應該也會使用這個 contextualIdentities API 。

contextualIdentities API 的問題

我去看過 contextualIdentities API 之後就覺得這真是個噁心霸道的東西,在棄用 legacy API 之後,插件沒辦法直接修改到 Firefox 的內部,所以照理來說 containers testpilot 是做不出來的,因為原本根本就沒有這種 WebExtensions API ——結果! Firefox 團隊就這樣直接幫 containers testpilot 開了後門,量身打造了一個 API 給他們——就是 contextualIdentities API 。

覺得很噁心,這不就剛好證明了能夠作出重大有意義的功能靠現有的 WebExtension API 根本做不到嗎?以前可以自己直接用 XPCOM ,現在還要先跟 Mozilla 開發團隊搞好關係才能在 core 塞進自己的插件需要的 WebExtensions API 。

撇開這個不說,這個 API 的設計看來也是一個 monolithic piece of poorly designed code 。正常來說 API 的設計應該是先暴露出最基礎的核心功能,然後由開發者自己組裝核心功能調用來作出有意義的插件吧?結果 contextualIdentities 這個 API 裡面直接把幾個在這個用途以外不相干的核心功能綁在一起暴露給 containers extension 使用:

  • 新增、刪除獨立的 cookie 儲存空間,一個新的 contextualIdentities API 會自動建立一個新的 cookie 儲存空間,完全沒有考量到獨立的 cookie 儲存空間可能會有 contextualIdentities 以外的用途,也沒有考慮到新的 contextual identity 想要分享現存 cookies 的狀況(共用一個 cookie 儲存空間)。
  • 更動右鍵選單項目,這個 API 直接自動新增了 “Open Link in New Container tab" 的項目。雖然我們已經有了 contextMenus API 可以新增右鍵選單項目,但顯然我們需要複製一份 code 給 contextualIdentities API 來讓使用者可以輕鬆地在新的 container 分頁裡面開啟連結,這樣複製一份 code 還可以減少 containers testpilot 插件開發者需要寫的 code ,非常棒。
  • 在 tab 頁籤上色的功能,在每個 contextual identity 裡面可以新增 tab ,在 contextual identity 當中新增的 tab 會自動被上色。顯然在分頁上色的功能只會有 containers 實驗會用到。
  • contextual identity 本身的頁籤顏色和圖示都只有固定的幾種可以選,圖示像是 cart, fingerprint, briefcase 等等,顯然不可能會有其他狀況下需要其他的圖示。

……正常的設計應該會是把前面三種功能分成三種不同的 API , 插件作者自己把這三種 API 結合在一起形成 contextual identity 的概念才對啊!!

另外,雖然有了 contextualIdentities API ,但還缺乏很多 API ,導致這些插件的功能還沒辦法實作出完全復刻原本 quicksaver 開發的 Tab Groups 的功能:

  • 如上所述,建立 contextual identity 的時候不能選擇共用 cookie store ,但 quicksaver 的版本裡面所有的分頁群組還是共用同一個 cookie store 的
    • 狀態:沒有查到相關的 feature request
  • 沒辦法選擇性只顯示部分的分頁(也就是只能顯示所有的分頁),quicksaver 的 tab groups 一次只會顯示一個群組。
  • 現在無法把某個 contextual identity 中的分頁移動到另一個 contextual identity ,只能關掉原本的分頁,然後開一個新的
  • 如果要使用類似 tree-style tabs ,把所有分頁和分頁群組放到側邊欄,那原本的橫向頁籤列應該要被隱藏,但現在沒有這樣的 API

相關資料、討論

個人感想

原本我以為沒希望了,更久以前我在搜尋的時候都找不到替代方案,幸好最近有 containers testpilot 所以才有這麼多延伸應用冒出來,有種燃起一線希望的感覺。但上面的那些 bug 大部分都還沒有被 mozilla 批准,我不太有信心 57 出來的時候分頁群組替代方案能夠完全提供舊有的功能,感覺上面當中一兩個還是很有可能會被 mozilla 拒絕…

更新

TabHide API 出爐之後我調查了幾個目前的插件,尋找符合我需求的,紀錄文在此

Android Island work profile apps and AFWall

Island is an app that allows you to isolate, clone, freeze, hide, archive apps.

Unfortunately when you use it with iptables firewall app like AFWall the apps placed in the work profile have no access to the network. (because there aren’t rules allowing them in the firewall)

AFWall doesn’t support multiple profiles – there’s an experimental option called “multi-user support" but only works in mode where selected items are blocked:

 

AFWall doesn’t show work profile apps. So I need a way to allow work profile app traffic.

With a bit of digging, I found the rule for work profile (UNIX) uid : user_id*100000+app_uid , the primary user has user id of 0, the work profile Island created has user id 10.

So you can go look for app uid, add user_id*100000 , and it becomes the (UNIX) uid of the app in that work profile.

A quick way to know app_uid is go to AFWall preferences > User interface > “Show UID for apps".

Then you need to write your own rules that allows traffic to pass through, the number after --uid-owner is the UID you want to change, in this example I allowed 3 apps:

Place it in /data/local/, chmod +x it, add its path to AFWall custom script, then apply rules in AFWall. You should now get internet in work profile apps.

寫爬蟲絕對不要太客氣也不可以心軟

尤其是要爬像是 Facebook 這種蓄意想要鎖住資訊,把 Internet 變成一個巨大內網的邪惡公司的時候。

之前在 Github 看到 rss-bridge 這個專案,用爬蟲去爬網頁然後產生 RSS/Atom feed ,原本是想要把它用在 Facebook 上面,這樣就可以訂閱粉專的每一篇貼文、照時間排序、不用被 Facebook 的 filter bubble 審查資訊,也不會因為你訂閱什麼就汙染你的 stream 。

架起來試了一下發現效果還不錯,訂閱了一堆粉專就去睡了。

結果隔天早上醒來發現所有 feed 都失效,因為 Facebook 要求 Captcha 驗證。原本的 rss-bridge 會把驗證圖片 proxy 顯示給瀏覽 feed 的使用者,使用者可以解完之後送出,rss-bridge 會再送給 Facebook ,但這功能實測的時候發現無論如何送出都會失敗,把這 bug 解掉之後又發現 captcha 只會當次有效,也就是要發另一個 request 的時候又會被跳 captcha 。

自己開瀏覽器測試了一下發現,瀏覽器的 captcha 只要解一次,未來的 request 就都會過,想說大概是因為 rss-bridge 的爬蟲不支援 cookie 的問題,所以就花了好幾天時間把 rss-bridge 的爬蟲換成 PHP curl (原本是用 file_get_contents() ),因為 PHP curl 有 cookie jar 的功能,管理 cookie 比較方便。

結果新的 code 終於會動,但儘管送了 cookie 還是每次被跳 captcha ,挖到底層去測試了很多東西,檢查 request header 和 response header ,確定不是我實作有問題之後,我發現問題不在 cookie。

問題在於 rss-bridge 的爬蟲預設都會多送一個 GET 參數 _fb_noscript=1 ,有這個參數的時候顯示出來的 captcha 會不一樣!

_fb_noscript=1 的時候顯示的 captcha 會直接是一個 <img src="..."> ,而且這個 captcha 也只有單次有效!下次再請求,就算有 cookie, 也會再被要求解另一個 captcha 。

沒有那個參數的時候的 captcha 圖片會使用 AJAX 載入,然後解開之後設定的 cookie 也會多次有效。

我拿舊的實作改成 curl 的時候沒有特別改這個東西,結果到現在才發現這件事。

所以,這次學到的教訓是,寫爬蟲的時候,不要仁慈,直接開 PhantomJS/CasperJS 吧……

P.S. 我改過的 rss-bridge 在這兒: https://github.com/pellaeon/rss-bridge/tree/enhance

更新:不想用 Javascript 的話 Python 的 Scrapy + Splash (Splash 是 headless browser)看起來還不錯(沒用過)。還有 django-dynamic-scraper 可以直接把爬到的東西結構化成 Django model object ,每一個 field 直接關聯一個 selector ,scrape 的時候讀進去,太神啦。

2017/2/24 更新2:後來又繼續試著解決這個問題。研究了一下 django-dynamic-scraper , 幾乎找不到如何處理 captcha 的說明和範例,雖然說應該是可以解但是感覺又要研究 scrapy 很久,所以還是決定回去幫 rss-bridge 加上 proxy 的功能。改出來的東西在 https://github.com/pellaeon/rss-bridge/tree/enhance2 。然後還花了一些心力研究要怎麼取得免費的 proxy 清單,最後發現 HideMyAss.com 的不錯,寫了個小小的 Javascript 去撈 proxy 清單

Fix building github.com/popcorn-official/popcorn-desktop

If you try to build from development branch, the result is not usable. I’ll explain the hacks you need to make it work.

Problem 1: 403 on nw:build

As reported on github: https://github.com/popcorn-official/popcorn-desktop/issues/202

Background on nw-builder

Basically what nw-builder does is that it fetches the prebuilt binaries of nw.js and copy the source code of your app and bundles it into a package. It is also possible to directly run your app without bundling it using the method described in the nw.js documentation:

cd /path/to/your/app
/path/to/nw .

According to this comment:

Just for you know that we have an strong dependency on NW, which is compiled PCT team.
PCT NW are compiled with anothers video codecs (something like that).
Maybe you should know this.

Note: I copy the entire contents from links in case of DMCA takedowns

Since the PCT binary URL is returns 403 now (not sure if it’s due to DMCA takedown), I thought about using the vanilla binaries instead, but this way we would lose a few codecs. Then I figured I can extract the binaries from the working packaged version 0.3.9 (it’s on the front page).

Download it, unzip it, copy Popcorn-Time.app to popcorn-desktop/cache/0.12.2/osx64/nwjs.app, and rm -r popcorn-desktop/cache/0.12.2/osx64/nwjs.app/Resources/app.nw otherwise nwjs will simply look for source code inside app.nw to run instead of your repo source code at popcorn-desktop.

Now we can start popcorntime from your source code with:

cd popcorntime-desktop
cache/0.12.2/osx64/nwjs.app/Contents/MacOS/nwjs –development .

Note: I read somewhere of the --development flag, but don’t observe any change in behavior, I’ll just leave it there.

Soon you’ll hit error like this (debug log at popcorn-desktop/cache/0.12.2/osx64/nwjs.app/Contents/MacOS/debug.log):

[43854:0710/142626:INFO:CONSOLE(27)] ""added" "TVShowTime" "to provider registry"", source: app://host/src/app/lib/providers/generic.js (27)
[43854:0710/142626:INFO:CONSOLE(35)] ""loaded" "tvshowtime.js"", source: app://host/src/app/bootstrap.js (35)
[43854:0710/142626:INFO:CONSOLE(27)] ""added" "Watchlist" "to provider registry"", source: app://host/src/app/lib/providers/generic.js (27)
[43854:0710/142626:INFO:CONSOLE(35)] ""loaded" "watchlist.js"", source: app://host/src/app/bootstrap.js (35)
[43854:0710/142626:INFO:CONSOLE(27)] ""added" "ysubs" "to provider registry"", source: app://host/src/app/lib/providers/generic.js (27)
[43854:0710/142626:INFO:CONSOLE(35)] ""loaded" "ysubs.js"", source: app://host/src/app/bootstrap.js (35)
[43854:0710/142626:INFO:CONSOLE(21)] ""%c[%cERROR%c] Error starting up" "color: black;" "color: red;" "color: black;"", source: app://host/src/app/app.js (21)
[43854:0710/142626:INFO:CONSOLE(16)] ""[%cWARNING%c] Show Disclaimer" "color: orange;" "color: black;"", source: app://host/src/app/app.js (16)

Problem 2: Error starting up

With my limited Javascript knowledge, I added the lines to print out debug messages at app/database.js#447:

446             .catch(function (err) {
447                 console.log(err.message);
448                 console.log(err.stack);
449                 win.error('Error starting up', err);

Got the error:

Error: Cannot find module 'butter-provider-MovieApi'
at Function.Module._resolveFilename (module.js:327:15)
at Function.Module._load (module.js:264:25)
at Module.require (module.js:356:17)
at require (module.js:375:17)
at window.require (\u003Canonymous\u003E:4:17)
at Object.getProvider [as get] (app://host/src/app/lib/providers/generic.js:54:60)
at app://host/src/app/lib/config.js:227:42
at Function._.map._.collect (app://host/src/app/vendor/underscore/underscore.js:172:24)
at Object.Config.getProviderForType (app://host/src/app/lib/config.js:226:26)
at app://host/src/app/bootstrap.js:85:42
at Array.map (native)
at app://host/src/app/bootstrap.js:83:56
at Promise_then_fulfilled (/Users/pellaeon/project/popcorn-desktop/node_modules/q/q.js:766:44)
at Promise_done_fulfilled (/Users/pellaeon/project/popcorn-desktop/node_modules/q/q.js:835:31)
at Fulfilled_dispatch [as dispatch] (/Users/pellaeon/project/popcorn-desktop/node_modules/q/q.js:1229:9)
at Pending_become_eachMessage_task (/Users/pellaeon/project/popcorn-desktop/node_modules/q/q.js:1369:30)"", source: app://host/src/app/database.js (448)

grepping butter-provider-MovieApi in the source tree returns no result, neither does googling.

Then I tried copying the entire working source tree from the packaged version Popcorn-Time.app/Contents/Resources/app.nw/, this doesn’t make the error go away either, which indicates that the problem lies not in the source tree. But where could the problem be?

The only one I could think of was the 2 files you need to download separately from popcorntime.sh to build it: popcorntime.sh/torrent_collection.js and popcorntime.sh/ysubs.js (instructions in readme). Anyway this doesn’t work either.

I came across issue#102:

The popcorntime.sh/ysubs.js downloaded is invalid, after pct load the file, it is not added to provider registry. I changed ysubs.js minified content by ysubs.js from branch feature/electron (old version of the file) and it works.

Hmmm, very suspicious.

But diffing ysubs.js doesn’t seem the downloaded version have anything different that would make the error go away, nor does actually installing it, nor is torrent_collection.js, which lies inside the view directory, even less likely.

So looks like I need to dig in to the trace to see what I have missed.

Digging into the code

With the trace above, I decided to go with bootstrap.js first, between line 83 and 85, let’s print out the variable first:

83             return _.filter(_.keys(Settings.providers).map(function (type) {
84                 console.log('========1');
85                 console.log(type);
86                 return {
87                     provider: App.Config.getProviderForType(type),
88                     type: type
89                 };

Running again, the log gives:

[46099:0710/164418:INFO:CONSOLE(84)] ""========1"", source: app://host/src/app/bootstrap.js (84)
[46099:0710/164418:INFO:CONSOLE(85)] ""movie"", source: app://host/src/app/bootstrap.js (85)

Hmmm, nothing too special, don’t look deeper just now.

Let’s see what it later calls: getProviderForType(). See the vars again:
config.js

225             } else if (provider instanceof Array || typeof provider === 'object') {
226                 return _.map(provider, function (t) {
227                     console.log('==========2');
228                     console.log(t);
229                     return App.Providers.get(t);
230                 });
231             } else {
232                 return App.Providers.get(provider);
233             }

Log:

[46099:0710/164418:INFO:CONSOLE(227)] ""==========2"", source: app://host/src/app/lib/config.js (227)
[46099:0710/164418:INFO:CONSOLE(228)] ""MovieApi?&apiURL=https://movies-v2.api-fetch.website/,"", source: app://host/src/app/lib/config.js (228)

…wait, isn’t that the non-existent module’s name? “butter-provider-MovieApi“, and after that a URL that doesn’t look valid at all: movies-v2.api-fetch.website, verryyy suspicious (Note: I later found out it is actually valid URL). grep it:

popcorn-desktop$ grep -R 'MovieApi' *
node_modules/butter-settings-popcorntime.io/index.js:             uri: ['MovieApi?'

What ?!! node_modules ! It gets settings from node_modules ?!

grep in packaged version returned no result. So this is what differs between 2 versions!

I copied index.js from packaged version to popcorn-desktop/node_modules..., and this time it started successfully, and is able to load all movies and TV shows!

What really happened

Looking at package.json from the 2 different butter-settings-popcorn.io directory, seems like the two do differ in versions, master uses c745869979405d02d5a4cfea066c227801ee0fc2 (of butter-settings-popcorn.io), 0.3.9 uses 6b8075e0065efbe7276e4a469ca00dbc43c2aaa9. diff:

git diff 6b8075e0065efbe7276e4a469ca00dbc43c2aaa9..HEAD
diff --git a/index.js b/index.js
index dce0e24..ccfcec1 100644
--- a/index.js
+++ b/index.js
@@ -16,10 +16,10 @@ module.exports = {
movie: {
order: 1,
name: 'Movies',
-             uri: ['yts?'
+             uri: ['MovieApi?'
+'&apiURL='
-                     + 'https://movies.api-fetch.website/,'
-//                     + 'cloudflare+https://xor.image.yt,'
+                     + 'https://movies-v2.api-fetch.website/,'
+//                     + 'cloudflare+https://movies-v2.api-fetch.website,'
//                     + 'cloudflare+http://xor.image.yt'
]
},
@@ -28,8 +28,8 @@ module.exports = {
name: 'Series',
uri: ['TVApi?'
+'&apiURL='
-                     + 'https://tv.api-fetch.website/,'
-//                     + 'https://odgoglfi7uddahby.onion.to/,'
+                     + 'https://tv-v2.api-fetch.website/,'
+//                     + 'cloudflare+https://tv-v2.api-fetch.website,'
//                     + 'http://tv.ytspt.re/'
]
},

Testing the URLs in it I found it is actually valid (must be the new gTLDs).

Seems like the name before the question mark is the key, there is butter-provider-yts but no butter-provider-MovieApi in node_modules, so it couldn’t load the latter and errored.

Butter will automatically load any npm package installed (listed in package.json) that matches the =/butter-provider-.*/ regex.

I guess this is in startup stage, and in later stage someone would request the MovieApi or yts provider, but I still haven’t found the code.

Anyway, this fixes the build, so another time!

Neutron VXLAN multicast – disable IGMP snooping

openstack-ansible liberty 6e3815d, l2pop=off

At first I encountered this issue: https://bugs.launchpad.net/openstack-ansible/liberty/+bug/1563448 , but after deleting vxlan interfaces and restarting neutron agents, there are still problems.

  1. Unicast packets in VM tenent networks works, which means from agent namespaces you can ping VM with their IP in that subnet.
  2. Broadcast packets in VM tenant networks doesn’t work, which means VM can’t get DHCP reply, nor ARP reply, and nor can the DHCP agents. but strangely in my case L3 agent can get VM’s ARP reply.
  3. After further investigation, I found that vxlan-X interface on compute and agent containers doesn’t receive broadcast packets that others sent.
  4. Further, on physical interface (in my case `eth1.30`, slave of  `br-vxlan`), I can see packets going out to `vxlan_group` (239.1.1.1 by default), but not packets from 239.1.1.1
  5. Packet capturing on the switch found that the switch is not forwarding the packets.
  6. On #openstack-ansible @jimmdenton pointed me this article: http://movingpackets.net/2013/06/04/multicast-problems-juniper-ex/ , suggest to disable IGMP snooping
  7. It works!

Thanks to Rackspace guys at #openstack-ansible, who spend a lot of time helping me debug the issue.

This article helped me understand how VXLAN and L2 population works: https://kimizhang.wordpress.com/2014/04/01/how-ml2vxlan-works/ . (Don’t use L2 population! Neutron developers suggest against it!)