Chrome Remote Desktop di Ubuntu 16

Ini berawal dari account TeamViewer saya yang diblokir karena dianggap untuk komersial. Sebenarnya untuk riset, tapi memang saya gunakan untuk mengendalikan sampai 4 PC, jadi ya wajar juga jika dicurigai 🙂

Beberapa alternatif opensource sulit untuk digunakan karena PC harus bisa diakses langsung atau harus memodifikasi router, padahal PC di kampus ditutup akses portnya.  Saya lihat Chrome Remote Dekstop (CRD) jadi alternatif yang menarik. Untuk me-remote Windows dengan CRD  lancar.  Masalah muncul saat saya gunakan pada Ubuntu 16. Layar memang muncul tapi hanya wallpaper saja.  Terminal masih bisa diakses melalui menu klik kanan. Tapi semua window tidak bisa digeser dan diresize! Ternyata berbeda dengan TeamViewer yang prinsipnya screen mirroring, di Ubuntu, CRD menggunakan virtual session. Dari yang saya baca, ada bug untuk session Unity yang diakses secara remote karena masalah 3D accelerator(?).

Googling, dapat solusi dari https://productforums.google.com/forum/#!msg/chrome/LJgIh-IJ9Lk/tj-SBgEd85wJ   Prinsipnya CRD dimodifikasi agar tidak membuat session baru, tetapi menggunakan session yang sudah ada. Solusi ini berhasil, dan saat dijalankan, maka layar host jadi gelap saat PC diakses remote.   Masalahnya, saat koneksi terputus, maka layar host akan tetap gelap dan reconnect pun gagal 😦  Googling tapi tidak menemukan solusi lain. Aneh ya, itu sebabnya saya buat posting blog ini.

Setelah saya lihat-lihat code di /opt/google/chrome-remote-desktop/chrome-remote-desktop (jangan lupa backup), di Ubuntu, desktop display manager yang digunakan adalah  /usr/sbin/lightdm-session. Secara default Ubuntu 16 menjalankan gnome-session –session=ubuntu dan ini yang menjadi sumber masalah. Sebenarnya ada code yang akan memilih gnome-session –session=ubuntu-2d jika tersedia di OS untuk menghindari bug akibat 3D accelerator. Masalahnya di Ubuntu 16, sudah tidak ada lagi gnome ubuntu-2d (hanya ada di Ubuntu 12). Saat saya coba install, sudah tidak ada dan tidak disupport.  Solusinya saya install gnome-session-flashback:

sudo apt-get update
sudo apt-get install gnome-session-flashback

Lalu edit (jangan lupa backup): /opt/google/chrome-remote-desktop/chrome-remote-desktop . Saya akan update bagian yang tadinya menggunakan ubuntu-2D

Stop dulu service chrome desktop:

/opt/google/chrome-remote-desktop/chrome-remote-desktop --stop

Edit bagian ini (cari dengan kata kunci “2d”):

  SESSION_WRAPPERS = [
    "/usr/sbin/lightdm-session",
    "/etc/gdm/Xsession",
    "/etc/X11/Xsession" ]
  for session_wrapper in SESSION_WRAPPERS:
    if os.path.exists(session_wrapper):
      #yw edit
      #if os.path.exists("/usr/bin/unity-2d-panel"):
        # On Ubuntu 12.04, the default session relies on 3D-accelerated
        # hardware. Trying to run this with a virtual X display produces
        # weird results on some systems (for example, upside-down and
        # corrupt displays).  So if the ubuntu-2d session is available,
        # choose it explicitly.
        return [session_wrapper, "/usr/lib/gnome-flashback/gnome-flashback-metacity"]
      #else:
        # Use the session wrapper by itself, and let the system choose a
        # session.
        #return [session_wrapper]
  return None

Intinya code akan menjalankan /usr/lib/gnome-flashback/gnome-flashback-metacity bukan gnome-session –session=ubuntu . Gunakan gnome metacity, bukan compiz karena metacity tidak menggunakan 3D accelerator.

Lalu dibagian lain edit juga:

x_session = choose_x_session()
#  yw ganti
if (len(x_session) == 2 and
   x_session[1] == "/usr/lib/gnome-flashback/gnome-flashback-metacity"):
       extra_x_args.extend(["-extension", "Composite"])

Katanya ini untuk mengatasi bug di Ubuntu 2D metacity, jadi saya juga tambahkan untuk gnome-flashback-metacity ini (entah valid atau tidak hehe).

restart ulang:

 /opt/google/chrome-remote-desktop/chrome-remote-desktop --start

Coba jalankan secara remote dan semua bisa berjalan dengan baik, walaupun user interfacenya agak berbeda dengan Ubuntu standard 🙂 CRD bahkan lebih cepat daripada TeamViewer.

Iklan

Eksplorasi Front-End

Setelah membuat model untuk task named entity, membuat model server, membuat web service maka selanjutnya adalah membuat web untuk mendemokan API yang dibuat.

Saya terakhir mengurusi front-end hampir 10 thn yang lalu untuk penelitian bersama pembimbing S2 saya. Waktu itu saya jadi belajar CSS dan Javascript. Saat lihat-lihat kondisi sekarang, wah sudah jauh berbeda. Ada berbagai macam framework: React, Angular, Vue.

Setelah saya eksplorasi, ternyata jauh berbeda dengan pendekatan lama.  Perlu belajar banyak. Masalahnya, framework juga bergeser dengan cepat. Bahkan untuk framework yang sama bisa mengalami perubahan besar (seperti Angular).  Kombinasi yang buruk terutama bagi saya yang punya waktu terbatas: perlu sumberdaya besar untuk belajar + gampang kadaluarsa ilmunya.

Akhirnya saya buat dengan cara lama saja, Python+Flask+Jinja2 +JQueryUI dan Bootstrap. Mungkin kalau benar-benar perlu serius, kami akan sewa orang lain saja 🙂

 

Web App dengan Python Flask, Nginx dan uwsgi.

Update April 2018:

Dengan Docker, proses persiapan dan deploy jauh lebih gampang. Tutorial/repo yang dapat digunakan: https://hub.docker.com/r/tiangolo/uwsgi-nginx-flask/

Walapun sempat mengerjakan beberapa proyek dengan PHP, pengalaman saya membuat web app dengan PHP ternyata tidak menyenangkan. Pernah coba menggunakan Java, tapi menurut saya terlalu rumit untuk web app. Sejalan dengan eksplorasi saya belajar Python untuk keperluan datamining, maka sekalian saja menggunakan Python untuk membuat applikasi web, baik frontend maupun untuk RestFul-API.

Di posting sebelumnya saya mencoba membuat sendiri dari nol, tapi ternyata rawan kesalahan. Setelah lihat-lihat beberapa framework, menurut saya yang paling menarik adalah Flask (http://flask.pocoo.org/).  Seperti biasa, saya share di blog ini untuk catatan pribadi dan mudah-mudahan bermanfaat bagi pemula seperti saya.

Saya menggunakan instance Ubuntu AWS  dengan client Windows+Putty.  Pertama siapkan port forwarding agar kita bisa mencoba app dengan aman via SSH.  Di Putty, pilih settings->connection-SSH-tunnels. Saya forward port 5000, jangan lupa klik add (gambar bawah). Simpan session supaya tidak perlu mengeset berulangkali.

tunnel

Pertamakali yang perlu dilakukan adalah install pip, tools untuk menginstall lib python.

sudo apt-get update
sudo apt-get install python-pip

Kemudian install nginx sebagai webserver

sudo apt-get install nginx

Pastikan port 80 sudah dibuka (kalau digital ocean dibuka, tapi kalau AWS defaultnya ditutup),  dan buka alamat server kita di browser, harusnya pesan bahwa nginx sudah terinstall akan keluar.

Selanjutnya kita akan menginstall virtual environment (virtualenv). Ini penting dan wajib karena memungkinkan kita menginstall lib Python untuk suatu proyek  yang terisolasi dengan proyek lainnya. Tanpa virtualenv, install lib bisa membuat proyek lain jadi tidak jalan (pengalaman pahit pribadi hehe).

sudo pip install virtualenv 

Selanjutnya buat directory project dan masuk.

mkdir myproject
cd myproject

Dalam direktori ini set virtual env dengan:

 virtualenv myproject-env 

untuk mengaktivasi virtual env ini, gunakan

source myproject-env/bin/activate 

perhatikan prompt akan berubah. Sekarang kalau kita jalankan pip, maka lib tersebut akan terinstall di virt env ini saja (myproject/myproject-env).  Tapi kalau installnya lewat apt-get tetap berlaku untuk semua.

Untuk mengetest, coba jalankan

 which python 

maka yang muncul adalah lokasi Python di myproject. Untuk menonaktifkan virt env ini,  ketik  deactivate (tapi jangan lakukan dulu sekarang).

Selanjutnya install Flask dan uwsgi. Flask untuk framework, sedangkan uwsgi untuk koneksi dengan nginx.

 pip install uwsgi flask 

Selanjutnya kita akan membuat code sederhana hello world.

from flask import Flask
from werkzeug.serving import run_simple
app = Flask(__name__)

@app.route("/")
def beranda():
     return "Hello World"

if __name__ == '__main__':
     run_simple('0.0.0.0', 5000, app, use_reloader=True, use_debugger=True, use_evalex=True)

Jalankan

 python myapp.py 

dan buka browser dengan alamat  http://localhost:5000/  maka di browser akan muncul “Hello World” (jika menggunakan putty, pastikan tunneling sudah benar).  Jika code diupdate maka otomatis akan dilakukan reload. Gampang kan 🙂

Inilah kelebihan Flask, sangat mudah untuk dijalankan dan didebug.  Tapi tentu jika sudah operasional cara seperti ini tidak cocok. Sekarang kita akan menghubungkan app ini dengan web server nginx melalui uwsgi.

Pertama buat konfigurasi wsgi dulu, buat file uwsgi-myproject.ini dengan isi sebagai berikut:

Catatan: Sesuaikan path lokasi project di variabel home. Pastikan nama directory virtenv sudah cocok.

[uwsgi]
home = /home/ubuntu/myproject
wsgi-file = %(home)/myapp.py
socket = 127.0.0.1:3033
callable = app
module = app
daemonize = %(home)/app.log
pidfile = %(home)/app.pid
virtualenv= %(home)/myproject-env

Sekarang kita perlu update nginx agar mendengarkan socket uwsgi.  Buka /etc/nginx/nginx.conf   (mungkin perlu sudo) lalu pada bagian  ” http { ” tambahkan konfigurasi server berikut:

http {
  ##
  # Basic Settings
  ##
  ...
  server {
    listen 80;
    location / { include uwsgi_params; uwsgi_pass 127.0.0.1:3033; }
  }
}

Selanjutnya hapus page default nginx dan restart nginx.

sudo rm -v /etc/nginx/sites-enabled/default
sudo service nginx restart

Sekarang jalankan uwsgi project kita dengan cara

 uwsgi uwsgi-myproject.ini 

Sekarang buka di browser alamat server anda (bukan localhost:5000), yang tadinya ucapan default dari nginx harusnya akan berubah menjadi “Hello world” . Jika tidak, coba lihat catatan error di di file app.log (tail -10 app.log)

Untuk men-stop uwsgi, lakukan dengan (hati-hati, parameternya adalah app.pid bukan file uwsgi.ini):

 uwsgi --stop app.pid 

Tips: jika di-log keluar pesan seperti ini: “probably another instance of uWSGI is running on the same address”. Reset port dengan

 sudo fuser -k 3033/tcp 

Untuk mengaktifkan autoreload gunakan:

 uwsgi --py-autoreload 1 

App mencari makan halal di Bali

Link ke app: https://play.google.com/store/apps/details?id=yuliadi.com.blhlv2

Ide pembuatan app ini sudah cukup lama. Saat saya datang ke Bali,  salah satu hal yang menyulitkan adalah mencari makan halal. Biasanya googling dulu, lalu tandai di peta. Tetapi ada beberapa tempat yang jarang dibahas orang, tidak mencantumkan halal di restorannya dan diwebsitenya padahal sebenarnya punya sertifikat halal MUI.  Ini diperparah situs MUI yang tidak menyedikan informasi yang mudah diakses masyarakat.

Sekitar sebulan yang lalu, untungnya ada pelatihan pengembangan Android dari Google untuk para dosen. Lumayan panjang 5 hari full time. Sambil ikut pelatihan saya mulai iseng mulai buat app ini.  Berlanjut saat saya ikut menemani istri ke Bali dalam rangka konferensi.

Saya tidak menduga ternyata perlu waktu banyak untuk mengumpulkan dan memverifikasi data.  Data dari MUI (pusat dan lokal Bali) berbentuk pdf dan isinya lebih fokus terhadap perusahaan yang mendaftarkan dan tidak ada informasi alamat.  Perlu dicek satu persatu dengan Google Streetview, Zomato dan Tripadvisor.

Setelah data tempat makan yang dapat sertifikat MUI, saya lanjutkan dengan data yang self-certified, artinya mengklaim halal dengan tulisan “halal” ditempel di depan tempat restoran atau diwebsitenya. Setelah googling tempat-tempat yang katanya halal, ternyata banyak yang sebenarnya tidak mengklaim dirinya halal karena menyajikan minuman alkohol. Orang mengganggap halal karena berasumsi karena tempat itu hanya menyajikan seafood, makanan timur tengah dan sunda.  Mungkin ke depannya saya akan tambahkan kategori no-pork tanpa atau dengan alkohol.

Dari sisi teknis, banyak yang saya pelajari. Mungkin nanti detilnya akan diposting terpisah. Ini beberapa yang saya ingat:

  • Recyleview, walaupun lebih ribet ternyata lebih cepat daripada listview.
  • Penggunaan key untuk mengakses Google Map API.  Ternyata key ini kalau ganti komputer harus dibuat lagi. Lalu saat publish jangan lupa diletakkan juga di /res/release.   Jika menggunakan Google Play App Signing, jangan lupa SHA1-nya ditambahkan ke key. Sempat panik saat coba app yang di playstore peta-nya blank 😦
  • Splashscreen dengan style tanpa load view, sangat membantu dari sisi user interface daripada user melihat layar kosong.
  • Layout dengan constraint layout mantep, mulai dari HP kecil sampai tablet bisa tetap bagus.
  • Karena ukuran APK saya besar (kena error), maka saya set proguard agar mengecilkan app. Tapi ini punya efek samping fasilitas search tidak jalan. Bug yang luar biasa (hanya error saat jadi APK). Harus tambahkan perkecualian di proguard untuk searchview.
  • Ada masalah penggunaan appcompat, ini saya lupa, tapi kalau tidak salah saat saya mengakses icon. Di HP istri (LG) ini menyebabkan error.
  • Ada beberapa device lokal (Andromax) yang mengalami error. Tapi karena saya tidak punya devicenya, masih belum bisa didebug.
  • Alpha dan beta test bisa dilakukan di playstore, tapi belum jelas caranya seperti apa.

 

Produk Digital

Saat ini kita  dapat melihat bagaimana kemampuan anak sampai dengan remaja mengkonsumsi produk digital. Mulai dari film, game, berita  sampai sosial media.  Jika itu dihilangkan dapat dipastikan mereka akan gelisah. Mereka lebih memilih akses internet daripada membeli barang seperti baju dan berwisata. Apakah mereka merasa tertekan dan sedih?  Kalau ditanya, biasanya mereka merasa cukup puas dan bahagia. Produk digital sudah memenuhi semua kebutuhan mereka (diluar sandang-pangan-papan).  Bagi mereka produk digital sudah seperti candu.

Generasi tipe ini hanya mengkonsumsi tapi sangat sedikit memproduksi.  Tidak ada masalah keseimbangan karena  produk digital sekali dibuat dapat direplikasi ribuan bahkan jutaan kali nyaris tanpa biaya. Satu orang bisa memproduksi belasan film di Youtube yang kemudian ditonton ratusan ribu orang.

Pergeseran ini juga terjadi di dunia kerja, perusahaan dengan puluhan pekerja bisa mempunyai pelanggan sampai jutaan orang. Tahun 2014, Whatsapp hanya memiliki 55 pegawai saat dibeli Facebook seharga 19 Milyar USD  (250 Triliun rupiah) dan sudah memiliki pengguna sebanyak 420 juta. Perkembangan kecerdasan buatan (AI) juga mengancam akan  menggantikan pekerjaan yang tadinya dianggap aman dari otomatisasi.

Menurut saya, di masa depan, masyarakat akan terbagi jadi dua kelompok. Ada kelompok minoritas yang punya kemampuan sangat tinggi dan sangat produktif dala menghasilkan produk digital dan di sisi lain kelompok mayoritas yang pasif dan hanya mengkonsumsi.  Bisa dipastikan kelompok minoritas akan memiliki kekayaan dan pengaruh lebih besar.

Ada dua pendekatan  untuk mengatasi kesenjangan ini. Pertama, dengan mempersiapkan sebanyak mungkin orang untuk bisa menghasilkan produk digital. Misalnya dengan pengenalan tentang ilmu komputasi untuk siswa SD-SMA dan materi elearning gratis seperti MOOC.  Kedua, dengan transfer kekayaan. Golongan minoritas dengan kekayaan berlimpah mendapat pajak tinggi dan ditransfer ke golongan mayoritas.  Artinya,  berikan uang yang cukup walaupun mereka menganggur  untuk sandang-pangan-papan-internet (hanya cukup, tidak berlebihan) dan biarkan mereka tenggelam dengan mengkonsumsi media digital.  Selama mereka puas, tidak akan terjadi kejahatan dan struktur sosial akan stabil (tidak ada kerusuhan).

Backend API dengan Python

Update: sebaiknya gunakan libray seperti Flask-Restfull. Tutorial saya tentang pengantar Flask+uwsgi+nginx

 

Saat ini saya mulai belajar Python karena Tensorflow (dan beberapa deeplearning framework) menggunakan Python. Lalu kebetulan di kuliah mobile programming saya memberikan tugas besar yang memerlukan backend untuk komunikasi antar app jadi sekalian saja untuk latihan.

Pada tugas mobile programming tersebut mahasiswa diminta membuat app yang mirip Gojek atau Uber tetapi untuk pedagang keliling.  Intinya agar pembeli bisa melihat pedagang keliling yang lewat. Jadi idenya saya akan buat REST  API dengan Python yang dapat digunakan untuk menyimpan dan berbagi  lokasi user (baik pedagang maupun pembeli). Niat utamanya untuk mencoba, niat sampingannya supaya tidak ada mahasiswa yang protes “kok kuliah mobile programming diminta buat backend” hehe.

Untuk server, saya buat droplet DigitalOcean. Bisa saja sih menggunakan server ilkom UPI, cuma gara-gara bulan lalu kena hack jadi males (tepatnya takut utak-atik). Install apache2, mysql, python dan setup Python sebagai CGI tidak susah. Cuma ternyata perlu hati-hati untuk versi python: versi 2.7 dan 3. Saya menggunakan python3, tapi saat install library,  lupa malah menggunakan pip, bukan pip3.  Sempat bingung kok librarynya tidak dikenal padahal sudah diinstall.

Dari sisi bahasa, Python yang menggunakan indentasi, bukan “{” “}” sempat membuat saya salah meletakan code, solusinya saya tambahkan comment seperti “#end for” 🙂 Yang paling susah adalah masalah protokol  HTTP-nya sendiri.  Misal setelah  Content-Type: ..” wajib ada satu baris kosong.  Lalu penanganan error (error 400, 500 dst). Anehnya jarang yang membahas ini, mungkin karena mayoritas menggunakan library seperti Flask? 

Berikut code untuk menyimpan lokasi:

#!/usr/bin/python3

# contoh cara penggunaan:  http://xxxyyy.co.id/updatelokasi.py?tag=yw&userid=123&lat=2.343434&long=1.2423424

import cgi, cgitb
cgitb.enable()

import MySQLdb

form = cgi.FieldStorage()

tag = form.getvalue('tag')
userid = form.getvalue('userid')
lat = form.getvalue('lat')
long = form.getvalue('long')

if (tag==None) or (userid==None) or (lat==None) or (long==None) :
   print ("Status: 400 Bad Request")
   print ("Content-Type: application/json\n")
   print ('{"status":"error parameter tidak lengkap, paramter yg valid adalah userid,tag,lat dan long"}')
   print("")
else:
   tag = cgi.escape(tag)
   #print (tag+":"+userid+":"+lat+":"+long)
   db = MySQLdb.connect("localhost","mobprog","mobprogpass","mobprog" )
   cursor = db.cursor()
   sql ="insert into lokasiuser (tag, userid, latitude, longitude) values (%s,%s,%s,%s)";
   try:
      cursor.execute(sql,(tag,userid,lat,long))
      db.commit()
      print ("Status: 200 OK")
      print ("Content-Type: application/json\n")
      print ('{"status":"OK"}')
      print("")
   except:
      db.rollback()
      print ("Status: 500 Internal Error")
      print ("Content-Type: application/json\n")
      print ('{"status":"Error di server, kontak yudi@upi.edu"}')
      print("")

   db.close()

Sedangkan code untuk mengambil lokasi:

#!/usr/bin/python3

# contoh cara penggunaan: http://xxxyyy.co.id/getlokasi.py?tag=yw&userid=123

import cgi, cgitb
cgitb.enable()

import MySQLdb
import json

form = cgi.FieldStorage()

tag = form.getvalue('tag')
userid = form.getvalue('userid')

if (tag==None) or (userid==None) :
   print ("Status: 400 Bad Request")
   print ("Content-Type: application/json\n")
   print ('{"error":"parameter tidak lengkap, paramter yg valid adalah userid dan tag"}')
   print("")
else:
   tag = cgi.escape(tag)
   #print (tag+":"+userid+":"+lat+":"+long)
   db = MySQLdb.connect("localhost","mobprog","mobprogpass","mobprog" )
   cursor = db.cursor()
   sql =" select timestamp, latitude, longitude  from lokasiuser where userid = %s and tag = %s ORDER BY timestamp DESC LIMIT 1"
   try:
      cursor.execute(sql,(userid,tag))
      results = cursor.fetchall()
      #harusnya cuma satu rec
      isAda = False
      for row in results:
         timestamp  = None
         timestamp  = row[0]
         latitude   = row[1]
         longitude  = row[2]
         response={"timestamp":timestamp.isoformat(),"latitude":latitude,"longitude":longitude}
         body = json.dumps(response)
         print ("Status: 200 OK")
         print ("Content-Type: application/json\n")
         print(body)
         print("")
         isAda = True
       #endfor
      if not(isAda) :
         print ("Status: 400 Bad Request")
         print ("Content-Type: application/json\n")
         print ('{"error":"userid atau tag tidak ditemukan"}')
         print("")

   except:
      print ("Status: 500 Internal Error");
      print("Content-Type: text/html\n")
      print('{"error":"server error, kontak yudi@upi.edu"}')
      print("")
   db.close()