/// BANGBOO BLOG ///
■23/10/31 10:57PM
GCP Network Connectivity
●共有 VPC
 同組織のプロジェクトのホストプロジェクト(親)のVPCをサービスプロジェクト(子)に共有●VPC ネットワーク ピアリング
 異なる組織間の接続(双方のVPCでコネクションを作成する、内部IPで通信する、サブネットは重複しないこと、2ホップ制限で1:1=3つ以上の場合は古メッシュでコネクション作成要)、k8sサービスとPod ipをVPCピアリング経由する利用法もある
●ハイブリッド サブネット Cloud VPN/Interconnect等が必要、オンプレルータとCloud RouterをBGPでつなぐ、オンプレとGCPをつなぐ
●Cloud Interconnect
 DCと専用線で閉域網接続、Cloud VPNより低レイテンシ/帯域安定●Cloud VPN オンプレとIPsec VPN接続、アドレス帯の重複だめ、Cloud VPN側でBGPIP設定やIKEキー生成をしオンプレルータ側でそれらを設定する

●内部範囲
 VPCで使うIPをCIDRで定義しIP範囲の使用方法を事前に決定しておく、IPが勝手に使われたりしない等ができる
●限定公開の Google アクセス(Private Google Access)
 外部IPを持たないGCE等はデフォルトのインターネットゲートウェイ0.0.0.0を経由してGoogle APIにアクセスする、VPC>Routesで見れる
●オンプレミス ホスト用の限定公開の Google アクセス CloudVPNやInterconnectを経由してオンプレから内部IPを利用してGoogleAPIにアクセス、GCP側ではCloudDNSで特定のドメインのAレコードを入れる、選択したドメインのIPアドレス範囲を静的カスタムルートでVPC内のプライベートIPからルーティングできるように設定する、オンプレにはCloudRouterからドメインのIPアドレス範囲をBGPでルーティング広報する、VPNやInterconnectがないと0.0.0.0でGoogleAPIにアクセスするがこれだとRFC1918に準拠しない199.33.153.4/30などのIPを使う必要がありルーティングが複雑化したり、オンプレを通る場合があり通信は慎重に設計をすること●Private Service Connect 「限定公開の Google アクセス」の発展版、オンプレをNATでVPCに接続、内部IPでGoogleAPIにアクセスできる、PSCエンドポイントを介して内部IPで公開できる、NATされ内部IPの公開先での重複OK

●プライベート サービス アクセス
 VPCペアリングを併用してサービスプロデューサをVPCに接続し内部IPで次のようなサービスに内部IPでアクセスできるようにする(Cloud VPNまたはInterconnectを付け足せばオンプレからも可)、Cloud SQL/AlloyDB for posgre/Memorystore for Redis/Memcached/Cloud build/Apigee等の限られたもの●サーバーレス VPC アクセス
 サーバレスからVPC内リソースにアクセスするためのコネクタ(通常は外部IP通信になるがコレだと内部IPでVPCにルーティングされる、/28のサブネットを指定)、例えば既存のcloud runサービスを編集しても付けられず初期構築時のみ設定できる
●外部 IP アドレスを持つ VM から API にアクセスする
IPv6をVMに設定し限定公開DNSゾーン設定をすればトラフィックはGCP内にとどまりインターネットを通りません

●CDN Interconnect
 Cloud CDNもあるが他社のCDNに接続する、Akamai/Cloud flare/fastly等々

●Network Connectivity Center 
 ハブとなりCloudVPN/InterconnectをメッシュしGCP/オンプレ含め通信させる、Googleのバックボーンでユーザ企業の拠点間を接続できる
●ダイレクト ピアリング
 GoogleのエッジNWに直接ピアリング接続を確立し高スループット化、Google workspaceやGoogleAPI用だが普通は使わずInterconnectを使う●キャリア ピアリング
 ダイレクトピアリングの高度な運用が自社対応できない等でサービスプロバイダ経由でGoogle workspaceなどのGoogleアプリに品質よくアクセスする

Google CloudのVPCを徹底解説!(応用編) - G-gen Tech Blog

●トンネル系の下記は色々権限が要りそうで候補
Compute OS login/IAP-secured tunnel user/Service account user/viewer/compute.instance*

■ポートフォワードは止めないと別につないで繋いでいるつもりでも同じところに繋ぎ続ける
lsof -i 3128
ps ax | grep 3128
ps ax | sshkill [PID]

■IAPトンネルexport http_proxy=http://localhost:3128export https_proxy=http://localhost:3128gcloud compute start-iap-tunnel --zone asia-northeast1-a gce-proxy001 3128 --local-host-port=localhost:3128 --project=gcp-proxy-prjでコマンドを打てばIAP踏み台トンネルを通って外部に通信できる
■踏み台コマンドgcloud compute ssh --projet gcp-prj-unco --zone asia-northeast1-a gce-step-svrでSSHログインしそこからcurl等で操作する

■シークレットからPWを取りつつコマンドを打つgcloud compute ssh --project gcp-prj --zone asia-northeast1-b stp-srv --tunnel-through-iap fNL 3306:mysql.com: 3306mysql -u baka_usr -p"$(gcloud secrets versions access latest --secret mysql_pw --project=gcp-prj)" -h 127.0.0.1-P 3306 mysqlコマンドのpオプションは空白なしでPWを入れる、baka_usrはMySQLのユーザ、mysql_pwはsecret mgrに保存した名前
 $()のLinuxコマンドでgcloudコマンドを入れ子。ワンライナーの形で利用ができるsecret mgrのコマンド  シークレット バージョンにアクセス  |  Secret Manager Documentation  |  Google Cloudワンライナー解説(変数と$()とバッククォート /// BANGBOO BLOG /// - Linux cmd
■SSHトンネル
sshの基本はこれ、セキュアシェル、トンネルは特殊?SSH [オプション] [ログイン名@]接続先 [接続先で実行するcmd]
接続先に権限があること
SSHの疎通確認ssh baka@stp_srv.unco.com
設定例.ssh/config User baka Hostname step_srv ProxyCommand ssh -W %h:%p baka@step_srv.unco.com PubkeyAuthentication no PasswordAuthentication yes
※sshはPWは危険なので鍵認証のみにしたい IPアドレス元を制限や同一IPのログイン試行は拒否する仕組み等は欲しい
SSHコネクション上でトンネル作るssh step_srv -L 8080:dest.benki.com:80 とか ssh -L 8080:dest.benki.com:80 ahouser@step_srv.unco.com※ポート22でstep_srvにSSHコネクションを貼り、ローカル:8080のリクエストはdest:80に転送する↓ブラウザか新規ターミナルでcurlhttp://localhost:8080ダメなら転送設定したターミナルはstep_srvにいるのでcurl

GCP、AWS、Azure 別に見るクラウド VM への攻撃経路まとめ (paloaltonetworks.jp)
=============
なぜレッドオーシャン化する前にサービスを グロースできなかったのか? - フリマアプリ編 - (フリル)サービスを急拡大させる意思決定が遅く競合に遅れ競合出現後も経営方針を大きく変えなかった勝利条件はユーザ数で機能差ではなかったパワープレーでいかにプロモーションばら撒いて認知広げて第一想起をとるかだった先行者優位で過ごせる期間は短いスタープレイヤーの採用、手数料無料化、TVCM等PLを超えた手法があった、BS経営すべきだった成長のキャップが創業者の能力になっていた有能な人材:耳の痛いことを言ってくれる人材を経営チームに採用しても良かったCTOが開発をし、組織運営の雑務をし、採用もやっていたCEOは机の組み立てをするな。CTOはPCの購入をするな役割の変化に素早く適用し権限移譲を行い、やるべきことをやれる状況を作るあるいは必要な組織を大きくすることに注力する、例えば開発組織を大きくする戦時のCEO、皆に戦時であることを伝える、企業文化に背く意思決定も行う研究や教育等、やった方が良さそうな耳障りの良いタスクも拒否するどうやったら市場で勝てるかの戦略↓
IPOとか目指さなければConfort zoneを見つけてじっくりまったりビジネスを継続させる手もある
メルカリやPay2をみた結果論、このやり方も古いというかアレ
視力回復の音
(16) HOKKORI 📽💫🐈🐢 on X: "視力回復してください❤️‍🩹😹 https://t.co/Zug4pEbvys" / X (twitter.com)
Comment (0)

■23/6/21 8:00PM
Machine learning(Bigquery ML)
機械学習: マシーンラーニング、ML。マッシーンがLearnしデータの背景にあるルールやパターンを発見する。モデル: 機械学習における入力データに対して結果(出力)を導き出す仕組み。モデルは入力されたデータを解析し、評価/判定を行った結果を出力として返す。つまり、機械学習は「入カ>モデル>出力」から成る。
学習データ モデルをつくるために学習させるデータ適用データ モデルに対して予測を適用させるデータ教師あり 学習データに対して正解ラベルを付けて学習する方法  例)過去にDMを送付した結果(目的変数)を用いて学習させる教師ありは、回帰と分類の2つに分けられます。 回帰(予测)  連続する数値を予測するもので、売上、重量、温度などを算出する 分類(識別)  データがどのクラスに属するかを予測するもので、販売商品が売れる見込み/売れない見込みなどに分類  DMにおいては分類モデル(買う・買わないの識別)を使用
目的変数 結果側の変数 例)ある特定のカテゴリやブランドをその顧客が注文する  ●確率(購入する・しない)   ー学習データにターゲットフラグ (購入あり=1、購入無し=0)を立てる   ー適用後に出てくる結果は0~1 (0%~100%) の範囲  ●金額説明変数(特微量とも呼ぶ)  原因側のデータ 目的変数を予測するために使用する顧客の様々な情報、大きく分けて2種類  ●顧客属性   ー法人/個人事業主など事業形態、登録住所の郵便番号、業種コードなど  ●購買履歴   ー購買 いつ、何を、どのくらい購入したか等の情報   ー各商品カテゴリ毎の購入金額 etc.質的変数 数値で推し測ることができ、数字の大小に意味をもつデータ  (例)売上、購入回数、従業員数、DM送付回数單的変数 単に分類や種類を区別するためだけのデータ  (例)都道府県、茎種コード、購入有無
統計手法 重回帰分析:  説明変数xが目的変数yの値を変化させます。そのため、説明変数から、目的変数の値を予測可能です。 ロジスティック回帰  yが1になる確率を判別します。いくつかの要因(説明変数)から「2値の結果(目的変数)」が起こる確率を説明・予測   2値の結果とは合格/不合格、採用/不採用など、答えが2つしかない場合を指し

線形回帰(エクセル)
 slope関数:回帰直線の傾きを取得
 inercept関数:回帰直線の切片を取得
 forecast関数:単回帰分析からの推測値を取得
+++++++++++++

BigQueryを利用して機械学習を簡易に学びたいです。Webアプリのログよりサーバーエラーを予測するようなもので方法を教えてください > BigQueryを利用して機械学習を学び、Webアプリのログからサーバーエラーを予測する方法について簡単に解説します。以下はその手順の概要です。
●ステップ1: データの準備まず、WebアプリのログデータをBigQueryにインポートします。ログデータには、タイムスタンプ、リクエストの詳細、ステータスコード、エラー メッセージなどの情報が含まれているはずです。
例:ログデータスキーマ server_logtimestamp: TIMESTAMPrequest_id: STRINGstatus_code: INTEGERerror_message: STRINGuser_agent: STRINGresponse_time: FLOAT
●ステップ2:データの前処理エラーログの解析を容易にするために、データをクリーンアップし、特徴量を作成します。
CREATE OR REPLACE TABLE ds.cleaned_log ASSELECTtimestamp,status_code,error_message,user_agent,response_time,IF(status_code >= 500, 1, 0) AS is_errorFROMds.server_logWHEREtimestamp BETWEEN 2023-01-01 AND 2023-12-31;
このクエリでは、ステータスコードが500以上のものをエラーと見なし、is_error列を追加しています。
●ステップ3:特徴量エンジニアリング機械学習モデルに投入するための特徴量を作成します。例えば、以下のような特徴量を考えます。-時間帯 (hour)-ユーザーエージェント (user_agent) -過去のエラーレート(past_error_rate)※past_error_rateを省くとどうなのか?
CREATE OR REPLACE TABLE ds.features ASSELECTEXTRACT(HOUR FROM timestamp) AS hour,user_agent,response_time,is_error,--AVG(is_error) OVER (PARTITION BY user_agent ORDER BY timestamp RANGE BETWEEN INTERVAL 1 DAY PRECEDING AND CURRENT ROW) AS past_error_rateAVG(is_error) OVER (PARTITION BY user_agent) AS past_error_rateFROMds.cleaned_log;
●ステップ4: 機械学習モデルの作成BigQuery MLを使用してモデルを作成します。ここではロジスティック回帰モデルを使用してエラーの予測を行います。
他には、The CREATE MODEL statement  |  BigQuery  |  Google Cloud
MODEL_TYPE = { 'LINEAR_REG' |'LOGISTIC_REG' |'KMEANS' |'MATRIX_FACTORIZATION' |'PCA' |'AUTOENCODER' |'AUTOML_CLASSIFIER' |'AUTOML_REGRESSOR' |'BOOSTED_TREE_CLASSIFIER' |'BOOSTED_TREE_REGRESSOR' |'RANDOM_FOREST_CLASSIFIER' |'RANDOM_FOREST_REGRESSOR' |'DNN_CLASSIFIER' |'DNN_REGRESSOR' |'DNN_LINEAR_COMBINED_CLASSIFIER' | 'DNN_LINEAR_COMBINED_REGRESSOR' |'ARIMA_PLUS' |'ARIMA_PLUS_XREG' |'TENSORFLOW' |'TENSORFLOW_LITE' |'ONNX' |'XGBOOST'}
CREATE OR REPLACE MODEL ds.error_prediction_modelOPTIONS(model_type='logistic_reg', input_label_cols=['is error'] ASSELECThour, user_agent, response_time, past_error_rate,is_errorFROMds.features;
●ステップ5: モデルの評価モデルを評価し、精度を確認します。
SELECT*FROM ML.EVALUATE(MODEL, `ds.error.prediction_model`,(SELECT hour,user_agent,response_time,past_error_rate,is_errorFROMds.features));
●ステップ6: エラーの予測新しいデータを使ってエラーを予測します。
SELECT*predicted_is_error,predicted_is_error_probsFROMML.PREDICT(MODEL `ds.error_prediction_model`,(SELECTEXTRACT(HOUR FROM timestamp) AS hour,user_agent,response_time,--AVG(CASE WHEN status_code 500 THEN 1 ELSE 0 END) OVER (PARTITION BY user_agent ORDER BY timestamp RANGE BETWEEN INTERVAL 1 DAY PRECEDING AND CURRENT ROW) AS past_error_rateAVG(CASE WHEN status_code >= 500 THEN 1 ELSE 0 END) OVER (PARTITION BY user_agent) AS past_error_rateFROMds.new_server_log));

●ダミーデータINSERT INTO ds.server_log (timestamp, request_id, status_code, error_message, user_agent, response_time)VALUES('2024-06-28 18:00:00 UTC', 'req 801, 208, '', 'Mozilla/5.0 (Windows NT 18.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36', 0.123), (2024-06-20 10:01:00 UTC', 'req 002, 588, Internal Server Error', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36', 8.456),(2024-06-28 10:02:00 UTC', 'req 003', 484, 'Not Found', 'Mozilla/5.0 (iPhone; CPU iPhone OS 14,6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.1 Mobile/15E148 Safari/604.1, 8.234),(2024-06-20 10:03:00 UTC', 'req 004', 200, '', 'Mozilla/5.0 (Windows NT 18.8; Win64; x64; rv:89.0) Gecko/20100181 Firefox/89.8, 0.345),(2024-06-28 10:04:00 UTC, 'req 005', 502, Bad Gateway', 'Mozilla/5.0 (Linux; Android 11; SM-G9918) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.8.4472.124 Mobile Safari/537.36, 0.678),(2024-86-28 10:05:00 UTC, 'req 006', 503, 'Service Unavailable', 'Mozilla/5.0 (iPad; CPU OS 14.6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.1 Mobile/15E148 Safari/6084.1, 0.789), (2824-86-28 18:06:00 UTC, req 007, 200, Chrome/91.0.4472.124 Safari/537.36, 0.567), Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko)(2024-06-2010:07:00 UTC, 'req 008, 500, Internal Server Error', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.8.4472.124 Safari/537.361, 0.890),(2024-06-20 18:08:00 UTC, req 009, 404, Not Found', 'Mozilla/5.0 (iPhone; CPU iPhone OS 14 6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.1 Mobile/15E148 Safari/604.11', 8.345),('2024-06-28 18:09:00 UTC', 'req 010', 200, '', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:89.0 Gecko/20100101 Firefox/89.0', 0.456);

INSERT INTO ds.new_server_log (timestamp, request_id, status_code, error_message, user_agent, response_time)VALUES(2024-06-21 09:00:00 UTC', 'req 101', 200, '', 'Mozilla/5.0 (Windows NT 18.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36', 0.112), (2024-06-21 09:01:08 UTC, req 102', 500, Internal Server Error', 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.8.4472.124 Safari/537.36, 0.478),(2024-06-21 09:02:00 UTC', 'req 183, 484, 'Not Found', 'Mozilla/5.0 (iPhone; CPU iPhone OS 14_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.1 Mobile/15E148 Safar1/684.1, 0.239),(2024-06-21 09:03:00 UTC', 'req 104, 200, Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:89.0) Gecko/20100101 Firefox/89.0, 8.301),
(2024-06-21 09:04:08 UTC, req 185', 502, 'Bad Gateway', 'Mozilla/5.0 (Linux; Android 11; SM-G9918) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.8.4472.124 Mobile Safari/537.36', 8.683),(2024-06-21 09:05:00 UTC, req 106', 503, Service Unavailable', 'Mozilla/5.0 (iPad; CPU OS 14,6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.1 Mobile/15E148 Safari/604.1, 0.756),
(2024-06-21 09:06:00 UTC, req 107, 208, ", Mozilla/5.0 (Windows NT 18.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.8.4472.124 Safari/537.36, 0.523),
(2024-06-21 09:07:00 UTC, req 188, 500, Internal Server Error, Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.8.4472.124 Safari/537.36, 0.812),('2024-06-21 09:08:08 UTC', 'req 109,, 404, 'Not Found', 'Mozilla/5.0 (iPhone: CPU iPhone OS 14,6 1ike Mac OS X) AppleWebKit/685.1.15 (KHTML, like Gecko) Version/14.1.1 Mobile/15E148 Safari/604.1', 0.267),('2024-06-21 09:09:08 UTC', 'req 110', 200, '', 'Mozilla/5.0 (Windows NT 18.8; Win64: x64; rv:89.0) Gecko/20180101 Firefox/89.8', 8.412);

Comment (0)

■23/6/1 12:00AM
GCP Python Google doc編集
Google Docのコピーや編集https://developers.google.com/docs/api/how-tos/documents?hl=jahttps://rimever.hatenablog.com/entry/2019/10/16/060000クイックスタートhttps://developers.google.com/docs/api/quickstart/python?hl=jaスコープ情報 https://developers.google.com/identity/protocols/oauth2/scopes?hl=ja#docsディスカバリ ドキュメント例えばこれはDriveAPIの分だが、RESTAPIで何ができるか全記載しているっぽいhttps://www.googleapis.com/discovery/v1/apis/drive/v3/restDrive APIhttps://developers.google.com/drive/api/guides/about-sdk?hl=jahttps://developers.google.com/drive/api/reference/rest/v3?hl=jaDocs APIhttps://developers.google.com/docs/api/concepts/document?hl=jahttps://developers.google.com/docs/api/reference/rest?hl=jahttps://googleapis.github.io/google-api-python-client/docs/epy/index.htmlhttps://googleapis.github.io/google-api-python-client/docs/dyn/docs_v1.htmlhttps://developers.google.com/docs/api/reference/rest/v1/documents/get?hl=ja

文字置換 https://developers.google.com/docs/api/how-tos/merge?hl=ja

※DocAPIからdriveld folderidは取得できなさそう、getは使えそう※DriveAPIが使えない?コピーでなくDocAPIでget body からの新規createで行く?共有ドライブ時は、supports All Drives=True が必要だったでOKfile_metadata = service.files().get(fileld=DOCUMENT_ID, fields=id, name, mimeType, driveld', supports AllDrives=True) execute()
サービスアカウントでGWSにアクセスするにはGWS OU設定等が必要な場合がある>Google一般共有Docで検証も可
あるGoogle Docをコピーし、本文を編集した上で 本文の編集は((sample))となっている文字列をAAAに置換する特定のドライブのフォルダに移動
from google.oauth2.service_account import Credentialsfrom googleapiclient.discoveryimport build import re#1. サービスアカウントの認証情報を設定SCOPES = ['https://www.googleapis.com/auth/documents','https://www.googleapis.com/auth/drive']SERVICE_ACCOUNT_FILE = 'path/to/your/service-account-file.json' #サービスアカウント のJSONファイルのパス
creds = Credentials.from_service_account_file(SERVICE_ACCOUNT_FILE, scopes=SCOPES)
#2. Google Docs と Driveのサービスをビルドdocs_service = build('docs', 'v1', credentials=creds)drive_service = build('drive', 'v3', credentials=creds)
#3. コピー元のGoogle DocのIDと、移動先のフォルダIDを設定SOURCE_DOCUMENT_ID = 'source_doc_id' #コピー元のドキュメントIDTARGET_FOLDER_ID = 'target_folder_id' #移動先のフォルダID
#4. Google Docをコピーcopied_doc = drive_service.files().copy(fileld=SOURCE_DOCUMENT_ID, body={"name": "Copied Document"), supportsAllDrives=True).execute()copied_doc_id = copied_doc['id']
#5、本文を取得し、{{sample}} をAAAに置換def replace_text(document_id, old_text, new_text) #ドキュメントの内容を取得 document = docs_service.documents().get(documentid=document_id).execute() content = document.get('body').get('content') #リクエストリスト requests = [] #検索と置換を行う for element in content: if 'paragraph' in element: for paragraph_element in element['paragraph']['elements']: if 'textRun' in paragraph_element: text = paragraph_element['textRun']['content'] if old_text in text: start_index = paragraph_element('startindex'] end_index = paragraph_element['endIndex'] requests append({ 'replaceAllText': { 'containsText': { 'text': re.escape(old_text), #エスケープなしにする必要有 'matchCase': True }, 'replaceText': new_text } }) #置換リクエストを実行 if requests: docs_service.documents().batchUpdate(documentid=document_id, body={'requests':requests}).execute()
#置換処理の実行replace_text(copied_doc_id, '((sample))', 'AAA')
#6、コピーしたドキュメントを指定のフォルダに移動drive_service.files().update(fileld=copied_doc_id, addParents=TARGET_FOLDER_ID, removeParents=copied doc['parents'][0], supportsAllDrives=True).execute() #親が取れないのでフォルダはハードコード
print(f"Document copied, edited, and moved successfully! Document ID: {copied_doc_id)")



Comment (0)

Navi: <  3 | 4 | 5 | 6  >
-Home
-Column [128]
-Europe [9]
-Gadget [77]
-Web [133]
-Bike [4]

@/// BANGBOO BLOG ///