etsuxのブログ

自分がハマったことなどを記録しています。

STYLE-IL11-i7S-HNV購入とVMware vSphere Hypervisorの構築

iiyamaの省スペースPCを購入。

iiyama STYLE-IL11-i7S-HNV [Windows 10 Home] | パソコン工房【公式通販】

大きさとスペックがよい自宅のセンターサーバー用(自分の実験用)。

 

ここにVMware vSphere Hypervisor環境を作って、仮想マシンをいくつも立てて、夢を見てみようと思う。

無償の VMware vSphere Hypervisor, 無償の仮想化 (ESXi)

VMware-VMvisor-Installer-201701001-4887370.x86_64.iso 

 

サイズが328 MBしかないのでISOをCD-Rに焼いて、インストールを始めたらすぐにエラーが出た。

「nfs41client failed to load」

How to fix nfs41client not load ? |VMware Communities

いろいろ書いてあるけど、ESXiとNICで調べてみるとNICが対応していないとダメらしい。つまり、エラーの原因は NICRealtekなので、ESXiに標準に提供されているドライバでは対応できない、ということらしい。

仮想環境なのでネットワークなしというわけにはいかないので、ネットワーク関連を無視してインストールするのはありえないから、対処は必須。

 

 

読みやすそうな参考記事はこのあたり。

VMware ESXiをインストールしたら「No network adapters」ではまった時の対処法 - Qiita

 

必要なファイルをダウンローしようとリンクをたどっていくと、ESXi-Customizer-PS-v2.5.ps1は飛び先がDonate(寄付)のところ。少し上にスクロールすれば、ダウンロードリンクがあるのでそこからダウンロード。

VMware Front Experience: ESXi-Customizer-PS

 

ダウンロードしたファイルは以下の3つ。

  • VMware-PowerCLI-6.5.0-4624819.exe
  • ESXi-Customizer-PS-v2.5.ps1
  • net51-drivers-1.0.0-1vft.510.0.0.799733.x86_64.vib

 

VMware-PowerCLIをインストールしようとすると、PowerShellのポリシー変更が必要とのこと。

コマンド:Set-ExecutionPolicy RemoteSigned

 

念のため、注意書きを読んでおく。とりあえず大丈夫とする。

about_Execution_Policies

RemoteSigned
- スクリプトを実行できます。Windows Server 2012 R2 の既定の実行ポリシーです。
- インターネット (電子メールとインスタント メッセージング プログラムを含む) からダウンロードされたスクリプトと構成ファイルが、信頼された発行元によってデジタル署名されている必要があります。
- ローカル コンピューター上で記述された (インターネットからダウンロードされていない) スクリプトには、デジタル署名は必要ありません。
- Unblock-File コマンドレットの使用などによってスクリプトのブロックが解除されている場合は、インターネットからダウンロードされた、署名されていないスクリプトが実行されます。
- インターネット以外をソースとする署名されていないスクリプトや、署名されているが悪意のあるスクリプトが実行される危険性があります。

 

PowerShellで実行したら

Set-ExecutionPolicy : レジストリ キー 'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\PowerS
' へのアクセスが拒否されました。 既定 (LocalMachine) のスコープの実行ポリシーを変更
ョンを使用して Windows PowerShell を起動してください。現在のユーザーの実行ポリシーを
-Scope CurrentUser" を実行してください。
発生場所 行:1 文字:1
+ Set-ExecutionPolicy RemoteSigned
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : PermissionDenied: (:) [Set-ExecutionPolicy], Unauthor
+ FullyQualifiedErrorId : System.UnauthorizedAccessException,Microsoft.PowerShe
nd

 

あ、PowerShellを「管理者として実行」で起動するんですね。→無事完了。

VMware-PowerCLIのインストールで[Continue]ボタンを押してインストール完了。

 

続いて、PowerShellESXi-Customizer-PSのスクリプトを実行。

about_Execution_Policiesの-v65の説明が間違っているけど。

-v60 : Create the latest ESXi 6.0 ISO
-v65 : Create the latest ESXi 6.0 ISO

 

以下のコマンドを実行。

.\ESXi-Customizer-PS-v2.5.ps1 -v65 -pkgDir O:\download

 

に不要なZIPファイルなどがあると読み込みに多少時間がかかる上にFAILEDが出て無視される。pkgDirには余計なファイルはおかないように注意。

 

Loading Offline bundles and VIB files from O:\download ...
Loading O:\download\XXXXXXXX.zip ... [FAILED]
Probably not a valid Offline bundle, ignoring.
Loading O:\download\net51-drivers-1.0.0-1vft.510.0.0.799733.x86_64.vib ... [OK]
Add VIB net51-drivers 1.0.0-1vft.510.0.0.799733 [New AcceptanceLevel: CommunitySupported] [OK, added]

 

カスタマイズ版のISOの作成が完了。

ESXi-6.5.0-20170404001-standard-customized.iso

 

標準のISOをCD-Rに焼いて(2回)でインストールに失敗したので、今度はDVD-RWに焼くことにした。常識かもしれないけど、↓のとおり。

  • DVD-RWでもISOイメージは焼ける。
  • 起動用のディスクに使える。
  • DVD-RWのほうがCD-RよりもESXiのインストールのファイルの読み込みが速い。(なぜかは知らない)

最初のエラーも起きなくなり、「nfs41client load successfully」で進んだ。

ESXiのインストール先にはSSDを選択したが、HDDにしておいて、SSDはOSの仮想イメージにしたほうがよかったかも。

 

PDCAサイクルも起承転結もフレームワークの1つなのに信奉しすぎ感が漂う

万能でないからこそフレームワークなのだが...
何か文章を書くのに「起承転結」の特に「転」って何だろう...
PDCAサイクルを回す」と呪文のように唱えても...
という私はまったくの素人で専門家ではない。ただの主観。

PDCAサイクルで難解なのは「A」で、さらに「A」から「P」へのつながり。

PDCAサイクルを回す」=「計画を立てて、それを実行して、振り返って、次に活かす」のような説明だが、これでは「A」がないし、「A」から「P」のつながりもわからない。

たぶん、こう説明する人は、作業工程を頭に思い浮かべているのかな?作業をするのに、計画して実行するだけではなく、ちゃんとチェックしないとダメだ、と言いたいのかな?と思ってしまう。

PDCAサイクルは工程管理ではなく、改善のスパイラルだということを忘れると「A」が抜け落ちてしまう。あと、「C」が「D」の生産物に対するチェック(レビューとか)と思っていたり。

わかりやすくなるかも、と思ってPDCAの日本語訳を考えてみた。

PDCA 一般訳 自己流訳  
Plan 計画 特化 具体化のアプローチ
Do 実行 実装
Check(Study) 評価(学習) 分解(解析) 抽象化のアプローチ
Action 活動 汎化

何かを作るときには、目的を満たすための「特化」したものを考え、それを「実装」する。よりよくするには、実装したものをいったん「分解」し、共通項を抽出して「汎化」する。汎化されたものは技術/部品/規約/知見などであり、次の特化したものを作るときに適用することで、よりよいものができるようになる。

一度書いたプログラムをリファクタリングして汎用性を高めたり、同じ轍を踏まない工夫をして、次に作るときに役立てることが「A」から「P」の流れとなる。

改善のスパイラルも同じで、計画/実行のプロセスを例えばKPT(Keep/Problem/Try)に分解して、よいものをよりよく汎用的にして、次の計画に取り入れる。

※個人的には問題のあるものを無理によくする改善策にとらわれず、障壁を人為的淘汰によって排除するだけにした方が実りあると思う。

ガントチャートを自作

興味本位でガントチャートを作ってみる。

サーバ側のエンジンを何にするかは、簡単なところでRails

画面も自作したり、jquery-uiでごりごり作ってもよいかもしれないが、既存のものを探してみる。

dhtmlxGanttでRailsのサンプルがあったので、これに逃げてしまおうかと。

Using dhtmlxGantt with Ruby on Rails [Tutorial]

 

tableのヘッダ固定+縦横スクロールでのセルの幅/高さ揃え

tableのヘッダ固定+縦横スクロールはいろいろなものが公開されているが、やっぱり自作もしてみる。

よくあるtaleを4つ用意してそれぞれをdivで囲み、右下のペインのスクロールバーで右上、左下が同期して動作する。

他と少し違うのは、FourPanesTable-fix-height、FourPanesTable-fix-width、FourPanesTable-fix-crossというクラスのスタイルで、幅と高さをそろえるための幅0pxのtd、高さ0xのtdを4つのペインの境目に入れたこと。(以下の図の矢印のところに、visibility: hiddenで隠された行や列がある)

f:id:etsux:20170620225933p:plain

four_panes_table.css

幅と高さ調整のスタイルの定義は以下ではまった。

  • 行を0pxにするには、line-heightとoverflow:hiddenが必要。
  • min-width、max-widthで幅、min-height、max-heightで高さを制御。
  • !importantをつけないと別で定義されたスタイルが使われる。
#FourPanesTable-left-top {
	margin: 0px;
	padding: 0px;
	vertical-align: top;
	text-align: left;
	float: left;
	overflow: hidden;
}
#FourPanesTable-right-top {
	margin: 0px;
	padding: 0px;
	vertical-align: top;
	text-align: left;
	overflow: hidden;
}
#FourPanesTable-left-bottom {
	margin: 0px;
	padding: 0px;
	vertical-align: top;
	text-align: left;
	float: left;
	overflow: hidden;
	height: 50vh;
}
#FourPanesTable-right-bottom {
	margin: 0px;
	padding: 0px;
	vertical-align: top;
	text-align: left;
	overflow: scroll;
	height: 50vh;
}
td.FourPanesTable-fix-height {
	min-width: 0px !important;
	max-width: 0px !important;
	min-height: 1em;
	max-height: 1em;
	height: 1em;
	visibility: hidden !important;
}
td.FourPanesTable-fix-width {
	min-height: 0px !important;
	max-height: 0px !important;
	line-height:0px !important;
	overflow: hidden !important;
	visibility: hidden !important;
}
td.FourPanesTable-fix-cross {
	min-width: 0px !important;
	max-width: 0px !important;
	min-height: 0px !important;
	max-height: 0px !important;
	line-height: 0px !important;
	overflow: hidden !important;
	visibility: hidden !important;
}
#FourPanesTable-left-top table ,
#FourPanesTable-right-top table ,
#FourPanesTable-left-bottom table ,
#FourPanesTable-right-bottom table {
	table-layout: fixed;
}
#FourPanesTable-left-bottom table tr:nth-child(odd),
#FourPanesTable-right-bottom table tr:nth-child(odd) {
	background: #f0ffff;
}

four_panes_table.js

$(this).scrollLeft()は()を忘れてスクロールが同期されずはまった。

$(document).ready(function() {
	$('#FourPanesTable-right-bottom').scroll(function(){
		$('#FourPanesTable-right-top').scrollLeft($(this).scrollLeft());
		$('#FourPanesTable-left-bottom').scrollTop($(this).scrollTop());
	});
});

Railsでdatetime_selectを操作

Railsでdatetime_selectを操作してみる。

一括でdisabled

jQueryでidの先頭一致を使って一括でdisabledにする。

$('[id^="xxx_yyy_"]').prop('disabled', true);

今日の日時を設定

:include_blank=>true指定がある場合で、空の要素の箇所だけ設定する。

function setVal(obj, val){
    if(obj.val() != "") return;
    obj.val(val);
}

Dateを取得して、その値を設定する。時間部は0埋め2桁なのでsliceで取得する。

now = new Date();
setVal($('#xxx_yyy_1i'), now.getFullYear());
setVal($('#xxx_yyy_2i'), now.getMonth()+1);
setVal($('#xxx_yyy_3i'), now.getDate());
setVal($('#xxx_yyy_4i'), ("0"+now.getHours()).slice(-2));
setVal($('#xxx_yyy_5i'), ("0"+now.getMinutes()).slice(-2));

時間部を表示/非表示

時のxxx_yyy_4i、分のxxx_yyy_5iを表示show/非表示hideで切り替えるが、コントロールだけ消してもセパレータが残る。

表示
$('#xxx_yyy_4i').show();
$('#xxx_yyy_5i').show();
非表示
$('#xxx_yyy_4i').hide();
$('#xxx_yyy_5i').hide();

上のようにすると、下のような表示になる。

[  ▼][  ▼][  ▼] — [  ▼] : [  ▼]
                      ↓ セパレータが残る
[  ▼][  ▼][  ▼] — :

セパレータは:datetime_separatorと:time_separatorで任意の文字が指定できるが、タグも指定できるようになっているので、spanタグでnameを指定して表示/非表示を切り替える。

:datetime_separator=>"<span name='datetime_separator'>&mdash;</span>",
:time_separator=>"<span name='datetime_separator'>:</span>"

指定したnameを使って一括で表示/非表示を切り替える。

表示
$('[name="datetime_separator"]').show();
非表示
$('[name="datetime_separator"]').hide();

Rubyで正規表現の一致部分に対する置換

Ruby正規表現の一致部分($1、$2、...)に対する置換をしたいのだが、できる方法が見つからない。

やりやいことは、例えば以下のような置換。

<div id="XXX" class="YYY" ~>~
↓divでclassがYYYの行に対してidとclassの後ろにAAAを追加する。
↓/<div\s+id=\"(\w+)\"\s+class=\"(YYY)\"/の$1と$2のマッチした部分を置換したい。
<div id="XXX-AAA" class="YYY-AAA" ~>~

正規表現で置換部分が1つだけ、かつ後方参照を使わなければ、以下でできる。

class String (Ruby 2.4.0)

self[regexp, nth] = val

× nthが複数必要な場合には対応できない。

× 後方参照が必要な場合も対応できない。

例えば、文字列で一致した一部をupcaseで置き換える場合はうまくいかない。

# ruby -e "str='abc'; str[/a(b)c/,1]=$1.upcase"
-e:1:in `<main>': undefined method `upcase' for nil:NilClass (NoMethodError)

当たり前だけど、先に評価すれば$1が設定されて、置き換えはできる。

# ruby -e "str='abc'; if /a(b)c/===str then; str[/a(b)c/,1]=$1.upcase; end; p str"
"aBc"

すでにやり方は確立されていそうだが、探しても見つからないので、簡単なメソッドを定義して実現してみる。

def replace_matches(str, re, vals)
	m = re.match(str)
	if m then
		(m.size-1).downto(1)do |i|
			next unless vals[i-1]
			ofs = m.offset(i)
			str[ofs[0], ofs[1]-ofs[0]] = eval(vals[i-1])
		end
	end
	return str
end

第1引数`str'は置換対象の文字列。

第2引数`re'は置換するための正規表現(後方参照する部分は重ねてはならない)。

第3引数`vals'は置換文字列の式で以下になる。

  • 文字列の配列で指定する。
  • 文字列は後でevalで評価するので式を書く。変換しない場合はnil
  • $1、$2、...はevalのときの後方参照で評価されるので、第2引数`re'の後方参照の指定に一致する。

返却値は変換後の文字列。一致しない場合は元の文字列。

require用にrm.rbに保存して実行して意図どおりの結果が出力されることを確認。

# ruby -r "./rm" -e "str='abc'; p replace_matches(str, /a(b)c/, [%Q{$1.upcase}])"
"aBc"

複数の場合も問題なし。$3を$1を使って置換するのもOK。

# ruby -r "./rm" -e "str='abc'; p replace_matches(str, /(a)(b)(c)/, [nil,%Q{$2.ord.to_s},%Q{'%02X' % $1.hex}])"
"a980A"

なお、後方参照を後方から置換しているので、MatchDataのoffsetがずれない。

前方から置換した場合
offset => [[0,1],[1,2],[2,3]]
abc => abc(変換なし)
abc => a98c
a98c => a90Ac
後方から置換した場合
offset => [[0,1],[1,2],[2,3]]
abc => ab0A
ab0A => a980A
a980A => a980A(変換なし)

 

サクラエディタで行指向のgrep置換マクロ

サクラエディタで行指向のgrep置換マクロを作成。

行指向のgrep置換というのは、grepした結果を行単位で反映する置換の方法。

単純な単語置換や正規表現の置換ではできない置換ができる。

例えば、見出しで番号を振っているテキストファイルの場合、連番が間違っていることは正規表現「^\d」でgrepするとわかりやすい。

test.txt(3,1): 1. 見出し
test.txt(5,1): 1.2. 見出し
test.txt(7,1): 1.2. 見出し
test.txt(10,1): 2. 見出し
test.txt(12,1): 2.1. 見出し
test.txt(14,1): 2.3. 見出し

この結果を直接編集して正しい番号を振りなおして置換するのが行指向のgrep置換。

test.txt(3,1): 1. 見出し
test.txt(5,1): 1.1. 見出し ← gepの結果を直接編集して直す
test.txt(7,1): 1.2. 見出し
test.txt(10,1): 2. 見出し
test.txt(12,1): 2.1. 見出し
test.txt(14,1): 2.2. 見出し ← gepの結果を直接編集して直す

grepの結果で置換すると以下のように変更が反映される。

タイトル

1. 見出し
記事
1.1. 見出し ← 直った
記事
1.2. 見出し
記事

2. 見出し
記事
2.1. 見出し
記事
2.2. 見出し ← 直った

 

いつもどおりVBScriptとかFileSystemObjectとかがわからず、かなりはまった。

以下を修正。

  • 改行コードは元のファイル(の先頭行)に合わせるようにして、改行コードを変えないようにした。(改行コードが差分で出て困るし、そもそも改行コードは変えるべきでない)
  • 最初にgrep置換を実行するか確認を出した。(割り付けキーのミスタイプでいきなり置換されるのは危険なので)
  • 2017/6/3 バックアップフォルダの親フォルダが作成されるようにした。
  • 2017/6/8 UTF-8の場合にBOMを削除するようにした。

マクロファイル:C:\Users\ユーザ名\AppData\Roaming\sakura\greprep.vbs

'###### 大域変数定義 ######
'置換したファイル数/行数
Dim repfcnt, replcnt
repfcnt = 0 '置換したファイル数
replcnt = 0 '置換した行数
'処理中のファイル名、行番号、文字コード
Dim file, lineno, charset
file = ""   '
lineno = 0
charset = GetCharset("SJIS") '文字コード
'ファイルオブジェクト/入出力オブジェクト
Dim fs, fi, fo
Set fs = CreateObject("Scripting.FileSystemObject")
Set fi = CreateObject("ADODB.Stream")
Set fo = CreateObject("ADODB.Stream")
'カレントディレクトリ、一時ディレクトリ
Dim curdir, tmpdir
Set curdir = fs.GetFolder(".")
Set tmpdir = fs.CreateFolder(fs.GetParentFolderName(curdir.path)+"\"+fs.GetTempName())
'正規表現オブジェクト
Dim re_grep, re_code, re_matches, re_match
'grep結果「ファイルパス(行番号,列番号): 置換内容」
Set re_grep = New RegExp
re_grep.Pattern = "^(.+?)\((\d+)(,\d+)?\): (.+)"
'文字コード文字コードセット:SJIS」など
Set re_code = New RegExp
re_code.Pattern = "^ +\(文字コードセット:(.+)\)"
'###### 主処理 ######
'最初に実行するか確認してから実行
rc = Editor.OkCancelBox("現在の編集内容でgrep置換を実行しますか?")
If rc = 1 Then
    GrepRepMain
End If
'###### サブルーチン ######
'grep置換本体
Sub GrepRepMain
    'grep結果に一致する行を置き換える
    allnum = Editor.GetLineCount(0)
    For i = 1 To allnum : Do
        '行を取得し、改行コードを削除する
        str = Chomp(Editor.GetLineStr(i))
        '複数のgrep結果がある場合に、空行を境とみなす
        If str = "" Then
            Finalize
            Exit Do
        End If
        '文字コード指定行の場合は文字コードを設定
        Set re_matches = re_code.Execute(str)
        If re_matches.Count > 0 Then
            Set re_match = re_matches(0)
            charset = GetCharset(re_match.submatches(0))
        End If
        'grep結果の行に一致した場合は置き換え
        Set re_matches = re_grep.Execute(str)
        If re_matches.Count > 0 Then
            'ファイルパス、行番号、対象行で行置換を呼び出し
            Set re_match = re_matches(0)
            GrepReplace re_match.submatches(0), CInt(re_match.submatches(1)), Chomp(re_match.submatches(3))
        End If
    Loop Until 1 : Next
    '残りの行の書き出し
    Finalize
    Editor.InfoMsg(""+CStr(repfcnt)+"ファイル、"+CStr(replcnt)+"行を置換しました。"+vbCrLf+tmpdir.path+"にバックアップがあります。")
End Sub
'対象行を置き換えるサブルーチン
Sub GrepReplace(t_file, t_lineno, t_str)
    '初回 or 前回のファイルと一致しない
    If t_file <> file Then
        '残りの書き込み
        Finalize
        '対象のファイルを設定、行番号の初期化
        file = t_file
        lineno = 0
        '書き込み用ファイルオブジェクトをオープン
        fo.Open
        fo.Type = 2 'テキスト
        fo.charset = charset '文字コード
        '読み込み対象ファイルをオープン
        fi.Open
        fi.Type = 2 'テキスト
        fi.charset = charset '文字コード
        fi.LineSeparator = 10 'LF
        fi.LoadFromFile file
        '1行読み込み、変換後の改行コードを設定
        If Right(fi.ReadText(-2), 1) = vbCr Then
            fo.LineSeparator = -1 'CR/LF
        Else
            fo.LineSeparator = 10 'LF
        End If
        fi.Position = 0
    End If
    ReplaceLine t_lineno, t_str
End Sub
'対象行を置き換える
'※t_linenoに0を指定することでファイルの最後まで読み込み/書き込み
Sub ReplaceLine(t_lineno, t_str)
    '1行ずつ読み込み、置換対象の行の場合は指定内容で置換
    Do While fi.EOS = False
        str = Chomp(fi.ReadText(-2))
        lineno = lineno + 1
        If lineno = t_lineno Then
            fo.WriteText t_str, 1
            replcnt = replcnt + 1
            Exit Do
        Else
            fo.WriteText str, 1
        End If
    Loop
End Sub
'残りの行の読み込み/書き込み、バックアップ取得、ファイル入替
Sub Finalize
    '対象ファイルがオープンしていなければ何もしない
    If file = "" Then
        Exit Sub
    End If
    '残りの行の読み込み/書き込み
    ReplaceLine 0, ""
    'UTF-8の場合はBOMを削除
    If fo.charset = "UTF-8" Then
        fo.Position = 0
        fo.Type = 1 'バイナリ
        fo.Position = 3
        data = fo.Read
        fo.Close
        fo.Open
        fo.Write(data)
    End If
    '一時ファイルに書き込みファイルクローズ
    ftmp = fs.GetTempName()
    fo.SaveToFile ftmp, 2
    fo.Close
    fi.Close
    'ファイル数のカウント
    repfcnt = repfcnt + 1
    '一時ディレクトリに同じパスでバックアップ用のファイル名を生成
    fbak = Replace(file, curdir.path, tmpdir.path)
    If fbak = file Then
        fbak = tmpdir.path + Replace(file, fs.GetDriveName, "")
    End If
    'バックアップ先のフォルダがなければ作成
    CreateParentFolder(fbak)
    'ファイルをバックアップして一時ファイルで置き換え
    fs.MoveFile file, fbak
    fs.MoveFile ftmp, file
    file = ""
End Sub
'親フォルダを再帰的に作成
Sub CreateParentFolder(path)
    parent = fs.GetParentFolderName(path)
    If Not fs.FolderExists(parent) Then
        CreateParentFolder(parent)
        fs.CreateFolder(parent)
    End If
End Sub
'###### 関数 ######
'改行コードを取り除く
Function Chomp(str)
    str = Replace(str, vbCr, "")
    Chomp = Replace(str, vbLf, "")
End Function
'文字コードの文字列を変換する
Function GetCharset(str)
    Select Case str
    Case "SJIS" GetCharset = "Shift_JIS"
    Case Else   GetCharset = str 'SJIS以外はとりあえず同じ文字列を返却
    End Select
End Function