간단한 클러스터 컴퓨팅을 구현해보면 재밌을 것 같아서 요즘에는 어떤 single board computer가 있는지 찾아보았다.

Best Single Board Computers 2016 기사를 보다가 Banana pi 라는 제품 사진을 봤는데, SATA 포트 처럼 생긴게 눈에 들어와서 스펙을 보니 정말로 SATA 포트가 있다;

관심이 생겨서 공식 사이트에서 정보를 더 찾아보니 라즈베리 파이와 비슷하게 만든 (RCA 단자도 있고) 모델도 있고, 하드웨어 스펙은 꽤 괜찮아보인다.

BPI-M3 제품 설명이 재미있다.

Unlike his careless brother, the M3 did not lose his SATA port.

메인 화면에 BPI-M2U, BPI-M64, BPI-M3 모델의 간단한 스펙과 사진이 있는데, M64만 SATA포트가 보이지 않았다. careless brother는 M64를 말하는듯 ㅎㅎ

왠지 중국 회사에서 만드는 것 같은데 우리나라 판매 사이트는 열리지가 않는다.

우분투에서는 기본적으로 GUI를 통해 파일관리자에서 서버에 연결을 이용해서 서버 주소를 입력하고 연결할 폴더를 고르는 방법으로 시놀로지 공유폴더를 마운트할 수 있다. 그러나 마운트되는 경로가 길고 복잡해서 커맨드라인에서 작업할 때 매우 불편하다.

마운트 되는 경로를 수동으로 직접 지정해주면 편리하게 사용할 수 있다. mount 명령을 아래와 같은 옵션과 파라미터로 실행하면 된다.

$ sudo mount -t cifs -o username=<사용자ID>,iocharset=utf8 //<서버주소>/<공유폴더이름> <마운트하고자 하는 위치>

-t cifs는 마운트하려는 파일시스템을 CIFS로 지정하겠다는 것이고, -o 뒤에는 옵션들이 붙는다. 사용자 이름을 지정하고 문자열 인코딩으로 utf8을 사용하도록 설정했다.

예를 들어 사용자 ID가 myid이고 주소가 192.168.0.2일 때 my_shared라는 공유폴더를 /mnt/my_shared에 마운트하고 싶다면 우선 /mnt/my_shared라는 디렉토리를 생성하고 아래와 같이 mount 명령을 실행한다.

$ sudo mount -t cifs -o username=myid,iocharset=utf8 //192.168.0.2/my_shared /mnt/my_shared

패스워드를 물어오는데 패스워드를 입력하고나면 마운트가 완료된다.

마운트를 해제하려면 다음을 실행한다.

$ umount /mnt/my_shared

This Python script downloads a Google Sheets document from Google Drive as an Excel file.

The code is mostly from the official Google API site; the changes I made are that Credential path is set to the directory where the script is excuted and that Scope is set from metadata.readonly to readonly for reading the content of files we want to download. (For more details on Scope, you can refer to the official document here)

Tested environment: Ubuntu 16.04, Python 3.5.2

Usage

To get a credential for this script, finish Step 1 following instructions here

Change the value of FILE_ID at line 27 to that of the actual file you want to download.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#! /usr/bin/env python3

from __future__ import print_function
import httplib2
import os

from apiclient import discovery
from oauth2client import client
from oauth2client import tools
from oauth2client.file import Storage

import io
from apiclient.http import MediaIoBaseDownload

try:
    import argparse
    flags = argparse.ArgumentParser(parents=[tools.argparser]).parse_args()
except ImportError:
    flags = None

CREDENTIAL_DIR = './credentials'
CREDENTIAL_FILENAME = 'drive-python-quickstart.json'
CLIENT_SECRET_FILE = 'client_secret.json'
APPLICATION_NAME = 'Google Drive File Export Example'
SCOPES = 'https://www.googleapis.com/auth/drive.readonly'

FILE_ID = 'your file id'
EXCEL = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
EXPORTED_FILE_NAME = 'exported_file.xlsx'

def get_credentials():
    credential_dir = CREDENTIAL_DIR
    if not os.path.exists(credential_dir):
        os.makedirs(credential_dir)
    credential_path = os.path.join(credential_dir, CREDENTIAL_FILENAME)

    store = Storage(credential_path)
    credentials = store.get()
    if not credentials or credentials.invalid:
        flow = client.flow_from_clientsecrets(CLIENT_SECRET_FILE, SCOPES)
        flow.user_agent = APPLICATION_NAME
        if flags:
            credentials = tools.run_flow(flow, store, flags)
        else:
            credentials = tools.run(flow, store)
        print('Storing credentials to ' + credential_path)
    return credentials

def main():
    credentials = get_credentials()
    http = credentials.authorize(httplib2.Http())
    service = discovery.build('drive', 'v3', http=http)

    request = service.files().export_media(fileId=FILE_ID, mimeType=EXCEL)
    fh = io.FileIO(EXPORTED_FILE_NAME, 'wb')
    downloader = MediaIoBaseDownload(fh, request)
    done = False
    while done is False:
        status, done = downloader.next_chunk()
        print('Download %d%%.' % int(status.progress() * 100))


if __name__ == '__main__':
    main()

If you run this script on a remote server via terminal, you should add --noauth_local_webserver option.

$ ./export_example.py --noauth_local_webserver

The script attempts to open an authorization URL to access to your Google Drive if the script has failed to find a valid credential. If you are running the script on a remote server, Lynx, a text web browser is executed to open the URL but Google Drive doesn’t accept a request from text browsers. This option provides a URL to proceed an authorization for you instead opening a text browser on the server, so you can just open your web browser and enter the URL.

After granting the permission, an authorization code will appear. Copy the code and paste it to the terminal.

Finished authorization, it starts downloading.

어느 날 Tip of the day에서 실행 타겟을 바꾸는 단축키를 봤다가 유용한 것 같아서 기억해둬야지 했는데 며칠 새 잊어버렸다. Tip of the day를 다시 열어서 아무리 next를 눌러도 안나오고, 인터넷에서 android studio shortcut 등으로 아무리 검색해도 나오지 않아서 좌절.

그런데 디버깅하다가 우연히 다시 발견하게 되었다.

Ctrl+Alt+R

원래는 breakpoint에서 Cmd+Alt+R을 눌러서 계속 진행하려고 했는데 손가락이 살짝 꼬여서 Cmd 대신 Ctrl이 눌렸던 것…

예전엔 최대한 많은 정보를 담기 위해 변수명을 길게 적는 것을 선호했다.

예를 들면 이런 식이었다. java private void someMethod(SomeValueClass someValueClass) { ... }

그런데 요즘엔 메서드 scope의 변수(매개변수, 로컬변수 등)는 그냥 value, listener 이렇게 간결하게 쓰는 게 더 읽기 쉬운 것 같다는 생각이 든다.

변수명이 너무 장황하면 코드의 구조와 흐름을 빠르게 파악하기 어렵다. 코드를 읽다가 변수를 발견하면 이 변수가 어디에서 어떻게 참조되는지 봐야하는데, 변수 이름이 너무 길면 머릿속에서 변수 이름끼리 비교하느라 집중력을 빼앗기는 것이다.

그런데 여전히 멤버변수는 가급적이면 클래스 이름을 그대로 살려서 짓는 것을 선호한다. 여러 곳에서 참조되는 변수 이름이 그냥 listener라고만 되어있으면 이게 무슨 리스너인지 알기 어렵다.

그리고 Jake Wharton이 지적했듯 이제 헝가리안 표기법은 버리는게 여러모로 좋을 것같다. 전에는 IDE의 도움을 가정하는 코딩을 별로 좋아하지 않았는데, 요즘엔 굳이 그럴 필요 있나 싶은 생각이 든다. 이제 이름 앞에 m, s같은 게 붙어있으면 왠지 잡티처럼 느껴진다. 변수이름이 눈에 잘 들어오지도 않고…